diff --git a/assets/animations/Desk.lottie b/assets/animations/Desk.lottie new file mode 100644 index 000000000000..15e6caa6be0c Binary files /dev/null and b/assets/animations/Desk.lottie differ diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 082cea49d771..2b4f480e9511 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -153,6 +153,7 @@ const ROUTES = { SETTINGS_STATUS_CLEAR_AFTER: 'settings/profile/status/clear-after', SETTINGS_STATUS_CLEAR_AFTER_DATE: 'settings/profile/status/clear-after/date', SETTINGS_STATUS_CLEAR_AFTER_TIME: 'settings/profile/status/clear-after/time', + SETTINGS_TROUBLESHOOT: 'settings/troubleshoot', KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 1626fdbd1898..3c54e613a1cc 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -35,6 +35,7 @@ const SCREENS = { CLOSE: 'Settings_Close', TWO_FACTOR_AUTH: 'Settings_TwoFactorAuth', REPORT_CARD_LOST_OR_DAMAGED: 'Settings_ReportCardLostOrDamaged', + TROUBLESHOOT: 'Settings_Troubleshoot', PROFILE: { ROOT: 'Settings_Profile', diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 252d13259aea..b6e277b39b5b 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -48,6 +48,7 @@ import Download from '@assets/images/download.svg'; import DragAndDrop from '@assets/images/drag-and-drop.svg'; import DragHandles from '@assets/images/drag-handles.svg'; import Emoji from '@assets/images/emoji.svg'; +import Lightbulb from '@assets/images/emojiCategoryIcons/light-bulb.svg'; import EmptyStateAttachReceipt from '@assets/images/empty-state__attach-receipt.svg'; import EmptyStateRoutePending from '@assets/images/emptystate__routepending.svg'; import EReceiptIcon from '@assets/images/eReceiptIcon.svg'; @@ -280,4 +281,5 @@ export { Instagram, ChatBubbleAdd, ChatBubbleUnread, + Lightbulb, }; diff --git a/src/components/LottieAnimations/index.tsx b/src/components/LottieAnimations/index.tsx index 10dc195365aa..808bc344af37 100644 --- a/src/components/LottieAnimations/index.tsx +++ b/src/components/LottieAnimations/index.tsx @@ -67,6 +67,11 @@ const DotLottieAnimations = { h: 240, backgroundColor: colors.yellow600, }, + Desk: { + file: require('@assets/animations/Desk.lottie'), + w: 200, + h: 120, + }, } satisfies Record; export default DotLottieAnimations; diff --git a/src/components/TestToolMenu.tsx b/src/components/TestToolMenu.tsx index 3766ed71a149..647f25c450e5 100644 --- a/src/components/TestToolMenu.tsx +++ b/src/components/TestToolMenu.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import useThemeStyles from '@hooks/useThemeStyles'; import * as ApiUtils from '@libs/ApiUtils'; import compose from '@libs/compose'; import * as Network from '@userActions/Network'; @@ -13,6 +14,7 @@ import Button from './Button'; import {withNetwork} from './OnyxProvider'; import Switch from './Switch'; import TestToolRow from './TestToolRow'; +import Text from './Text'; type TestToolMenuOnyxProps = { /** User object in Onyx */ @@ -27,9 +29,16 @@ const USER_DEFAULT: UserOnyx = {shouldUseStagingServer: undefined, isSubscribedT function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) { const shouldUseStagingServer = user?.shouldUseStagingServer ?? ApiUtils.isUsingStagingApi(); + const styles = useThemeStyles(); return ( <> + + Test Preferences + {/* Option to switch between staging and default api endpoints. This enables QA, internal testers and external devs to take advantage of sandbox environments for 3rd party services like Plaid and Onfido. This toggle is not rendered for internal devs as they make environment changes directly to the .env file. */} diff --git a/src/languages/en.ts b/src/languages/en.ts index c705541216ab..c4698974c3b1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -810,6 +810,7 @@ export default { viewTheCode: 'View the code', viewOpenJobs: 'View open jobs', reportABug: 'Report a bug', + troubleshoot: 'Troubleshoot', }, appDownloadLinks: { android: { @@ -822,6 +823,14 @@ export default { label: 'macOS', }, }, + troubleshoot: { + clearCacheAndRestart: 'Clear cache and restart', + viewConsole: 'View debug console', + description: 'Use the tools below to help troubleshoot the Expensify experience. If you encounter any issues, please', + submitBug: 'submit a bug', + confirmResetDescription: 'All unsent draft messages will be lost, but the rest of your data is safe.', + resetAndRefresh: 'Reset and refresh', + }, goToExpensifyClassic: 'Go to Expensify Classic', security: 'Security', signOut: 'Sign out', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6d853604aa20..74d7b634fc81 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -805,6 +805,7 @@ export default { viewTheCode: 'Ver código', viewOpenJobs: 'Ver trabajos disponibles', reportABug: 'Reportar un error', + troubleshoot: 'Solución de problemas', }, appDownloadLinks: { android: { @@ -817,6 +818,14 @@ export default { label: 'macOS', }, }, + troubleshoot: { + clearCacheAndRestart: 'Borrar caché y reiniciar', + viewConsole: 'Ver la consola de depuración', + description: 'Utilice las herramientas que aparecen a continuación para solucionar los problemas de Expensify. Si tiene algún problema, por favor', + submitBug: 'envíe un error', + confirmResetDescription: 'Todos los borradores no enviados se perderán, pero el resto de tus datos estarán a salvo.', + resetAndRefresh: 'Restablecer y actualizar', + }, security: 'Seguridad', signOut: 'Desconectar', signOutConfirmationText: 'Si cierras sesión perderás los cambios hechos mientras estabas desconectado', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 4723bbfd9b4e..b573161b3342 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -220,6 +220,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/settings/Security/CloseAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: () => require('../../../pages/settings/AppDownloadLinks').default as React.ComponentType, [SCREENS.SETTINGS.LOUNGE_ACCESS]: () => require('../../../pages/settings/Profile/LoungeAccessPage').default as React.ComponentType, + [SCREENS.SETTINGS.TROUBLESHOOT]: () => require('../../../pages/settings/AboutPage/TroubleshootPage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: () => require('../../../pages/settings/Profile/PersonalDetails/AddressPage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.DOMAIN_CARD]: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.REPORT_VIRTUAL_CARD_FRAUD]: () => require('../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index 3dcffae95861..427ca2251e88 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -34,7 +34,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS, ], [SCREENS.SETTINGS.SECURITY]: [SCREENS.SETTINGS.TWO_FACTOR_AUTH, SCREENS.SETTINGS.CLOSE], - [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.KEYBOARD_SHORTCUTS], + [SCREENS.SETTINGS.ABOUT]: [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS, SCREENS.KEYBOARD_SHORTCUTS, SCREENS.SETTINGS.TROUBLESHOOT], }; export default FULL_SCREEN_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 52e512d32c59..a4d30b2e20d0 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -176,6 +176,10 @@ const config: LinkingOptions['config'] = { path: ROUTES.SETTINGS_APP_DOWNLOAD_LINKS, exact: true, }, + [SCREENS.SETTINGS.TROUBLESHOOT]: { + path: ROUTES.SETTINGS_TROUBLESHOOT, + exact: true, + }, [SCREENS.SETTINGS.PROFILE.CONTACT_METHODS]: { path: ROUTES.SETTINGS_CONTACT_METHODS.route, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 7e6a13ec7ffc..6a06b0a4a6b1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -103,6 +103,7 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.SECURITY]: undefined; [SCREENS.SETTINGS.ABOUT]: undefined; [SCREENS.SETTINGS.APP_DOWNLOAD_LINKS]: undefined; + [SCREENS.SETTINGS.TROUBLESHOOT]: undefined; [SCREENS.SETTINGS.LOUNGE_ACCESS]: undefined; [SCREENS.SETTINGS.WALLET.ROOT]: undefined; [SCREENS.SETTINGS.WALLET.CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS]: undefined; diff --git a/src/pages/settings/AboutPage/AboutPage.tsx b/src/pages/settings/AboutPage/AboutPage.tsx index 26d845a6a921..37da06ba32f3 100644 --- a/src/pages/settings/AboutPage/AboutPage.tsx +++ b/src/pages/settings/AboutPage/AboutPage.tsx @@ -86,6 +86,11 @@ function AboutPage() { }, link: CONST.UPWORK_URL, }, + { + translationKey: 'initialSettingsPage.aboutPage.troubleshoot', + icon: Expensicons.Lightbulb, + action: waitForNavigate(() => Navigation.navigate(ROUTES.SETTINGS_TROUBLESHOOT)), + }, { translationKey: 'initialSettingsPage.aboutPage.reportABug', icon: Expensicons.Bug, diff --git a/src/pages/settings/AboutPage/TroubleshootPage.tsx b/src/pages/settings/AboutPage/TroubleshootPage.tsx new file mode 100644 index 000000000000..6bb369e62474 --- /dev/null +++ b/src/pages/settings/AboutPage/TroubleshootPage.tsx @@ -0,0 +1,120 @@ +import React, {useMemo, useState} from 'react'; +import {View} from 'react-native'; +import Onyx from 'react-native-onyx'; +import type {SvgProps} from 'react-native-svg'; +import ConfirmModal from '@components/ConfirmModal'; +import * as Expensicons from '@components/Icon/Expensicons'; +import IllustratedHeaderPageLayout from '@components/IllustratedHeaderPageLayout'; +import LottieAnimations from '@components/LottieAnimations'; +import MenuItemList from '@components/MenuItemList'; +import TestToolMenu from '@components/TestToolMenu'; +import Text from '@components/Text'; +import TextLink from '@components/TextLink'; +import useEnvironment from '@hooks/useEnvironment'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +import * as App from '@userActions/App'; +import * as Report from '@userActions/Report'; +import type {TranslationPaths} from '@src/languages/types'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {OnyxKey} from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import SCREENS from '@src/SCREENS'; + +const keysToPreserve: OnyxKey[] = [ + ONYXKEYS.ACCOUNT, + ONYXKEYS.IS_CHECKING_PUBLIC_ROOM, + ONYXKEYS.IS_LOADING_APP, + ONYXKEYS.IS_SIDEBAR_LOADED, + ONYXKEYS.MODAL, + ONYXKEYS.NETWORK, + ONYXKEYS.SESSION, + ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT, + ONYXKEYS.NVP_TRY_FOCUS_MODE, + ONYXKEYS.PREFERRED_THEME, + ONYXKEYS.NVP_PREFERRED_LOCALE, +]; + +type BaseMenuItem = { + translationKey: TranslationPaths; + icon: React.FC; + action: () => void; +}; + +function TroubleshootPage() { + const {translate} = useLocalize(); + const theme = useTheme(); + const styles = useThemeStyles(); + const {isProduction} = useEnvironment(); + const [isConfirmationModalVisible, setIsConfirmationModalVisible] = useState(false); + + const menuItems = useMemo(() => { + const baseMenuItems: BaseMenuItem[] = [ + { + translationKey: 'initialSettingsPage.troubleshoot.clearCacheAndRestart', + icon: Expensicons.RotateLeft, + action: () => setIsConfirmationModalVisible(true), + }, + ]; + + return baseMenuItems.map((item) => ({ + key: item.translationKey, + title: translate(item.translationKey), + icon: item.icon, + onPress: item.action, + })); + }, [translate]); + + return ( + Navigation.goBack(ROUTES.SETTINGS_ABOUT)} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.TROUBLESHOOT].backgroundColor} + illustration={LottieAnimations.Desk} + testID={TroubleshootPage.displayName} + > + + {translate('initialSettingsPage.aboutPage.troubleshoot')} + + {translate('initialSettingsPage.troubleshoot.description')}{' '} + Report.navigateToConciergeChat()} + > + {translate('initialSettingsPage.troubleshoot.submitBug')} + + + + + {/* Enable additional test features in non-production environments */} + {!isProduction && ( + + + + )} + { + setIsConfirmationModalVisible(false); + Onyx.clear(keysToPreserve).then(() => { + App.openApp(); + }); + }} + onCancel={() => setIsConfirmationModalVisible(false)} + prompt={translate('initialSettingsPage.troubleshoot.confirmResetDescription')} + confirmText={translate('initialSettingsPage.troubleshoot.resetAndRefresh')} + cancelText={translate('common.cancel')} + /> + + ); +} + +TroubleshootPage.displayName = 'TroubleshootPage'; + +export default TroubleshootPage; diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.js index 5ac78f6d20c6..0fd6121fe512 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.js @@ -10,9 +10,7 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import ScreenWrapper from '@components/ScreenWrapper'; import Section from '@components/Section'; import Switch from '@components/Switch'; -import TestToolMenu from '@components/TestToolMenu'; import Text from '@components/Text'; -import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -44,7 +42,6 @@ const defaultProps = { function PreferencesPage(props) { const styles = useThemeStyles(); - const {isProduction} = useEnvironment(); const {translate, preferredLocale} = useLocalize(); const {isSmallScreenWidth} = useWindowDimensions(); @@ -125,19 +122,6 @@ function PreferencesPage(props) { /> - {!isProduction && ( -
- - - -
- )} diff --git a/src/styles/theme/themes/dark.ts b/src/styles/theme/themes/dark.ts index 1230c4ae03c2..bce0145099b9 100644 --- a/src/styles/theme/themes/dark.ts +++ b/src/styles/theme/themes/dark.ts @@ -128,6 +128,10 @@ const darkTheme = { backgroundColor: colors.productDark100, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, }, + [SCREENS.SETTINGS.TROUBLESHOOT]: { + backgroundColor: colors.blue700, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, [SCREENS.REFERRAL_DETAILS]: { backgroundColor: colors.pink800, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, diff --git a/src/styles/theme/themes/light.ts b/src/styles/theme/themes/light.ts index ad0bdfb296ce..40a9d27bcbc3 100644 --- a/src/styles/theme/themes/light.ts +++ b/src/styles/theme/themes/light.ts @@ -128,6 +128,10 @@ const lightTheme = { backgroundColor: colors.productLight100, statusBarStyle: CONST.STATUS_BAR_STYLE.DARK_CONTENT, }, + [SCREENS.SETTINGS.TROUBLESHOOT]: { + backgroundColor: colors.blue700, + statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT, + }, [SCREENS.REFERRAL_DETAILS]: { backgroundColor: colors.pink800, statusBarStyle: CONST.STATUS_BAR_STYLE.LIGHT_CONTENT,