From 8f4e247fca310cf7b9548d65ea8ffd9a62b3b898 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 11:12:18 -0800 Subject: [PATCH 01/81] Remove unnecessary directory --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 2 +- src/pages/settings/{AboutPage => }/AboutPage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/pages/settings/{AboutPage => }/AboutPage.js (99%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index b0f33af0ce2e..89365d1e0224 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -203,7 +203,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP [SCREENS.SETTINGS.PREFERENCES.THEME]: () => require('../../../pages/settings/Preferences/ThemePage').default as React.ComponentType, [SCREENS.SETTINGS.CLOSE]: () => require('../../../pages/settings/Security/CloseAccountPage').default as React.ComponentType, [SCREENS.SETTINGS.SECURITY]: () => require('../../../pages/settings/Security/SecuritySettingsPage').default as React.ComponentType, - [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType, + [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage').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.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, diff --git a/src/pages/settings/AboutPage/AboutPage.js b/src/pages/settings/AboutPage.js similarity index 99% rename from src/pages/settings/AboutPage/AboutPage.js rename to src/pages/settings/AboutPage.js index a460c95cdfe6..1ec6c119a5a6 100644 --- a/src/pages/settings/AboutPage/AboutPage.js +++ b/src/pages/settings/AboutPage.js @@ -22,7 +22,7 @@ import * as Report from '@userActions/Report'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -import pkg from '../../../../package.json'; +import pkg from '../../../package.json'; const propTypes = { ...withLocalizePropTypes, From d9d97caa2e3ceff968449684c43938eafe7fafec Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 11:55:15 -0800 Subject: [PATCH 02/81] Set up pages and navigation --- src/ROUTES.ts | 13 ++++++++ src/SCREENS.ts | 6 ++++ .../AppNavigator/ModalStackNavigators.tsx | 3 ++ src/libs/Navigation/linkingConfig.ts | 9 ++++++ .../ExitSurvey/ExitSurveyConfirmPage.tsx | 30 +++++++++++++++++++ .../ExitSurvey/ExitSurveyReasonPage.tsx | 27 +++++++++++++++++ .../ExitSurvey/ExitSurveyResponsePage.tsx | 27 +++++++++++++++++ src/pages/settings/InitialSettingsPage.js | 5 +--- 8 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx create mode 100644 src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx create mode 100644 src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 37003a09a0cd..b59348e5edf9 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -144,6 +144,19 @@ const ROUTES = { SETTINGS_STATUS_CLEAR_AFTER_DATE: 'settings/profile/status/clear-after/date', SETTINGS_STATUS_CLEAR_AFTER_TIME: 'settings/profile/status/clear-after/time', + SETTINGS_EXIT_SURVEY_REASON: { + route: 'settings/exit-survey/reason', + getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/reason', backTo), + }, + SETTINGS_EXIT_SURVEY_RESPONSE: { + route: 'settings/exit-survey/response', + getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/response', backTo), + }, + SETTINGS_EXIT_SURVEY_CONFIRM: { + route: 'settings/exit-survey/confirm', + getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/confirm', backTo), + }, + KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', NEW: 'new', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 703cb309d641..2b630aab62c8 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -77,6 +77,12 @@ const SCREENS = { REPORT_VIRTUAL_CARD_FRAUD: 'Settings_Wallet_ReportVirtualCardFraud', CARDS_DIGITAL_DETAILS_UPDATE_ADDRESS: 'Settings_Wallet_Cards_Digital_Details_Update_Address', }, + + EXIT_SURVEY: { + REASON: 'Settings_ExitSurvey_Reason', + RESPONSE: 'Settings_ExitSurvey_Response', + CONFIRM: 'Settings_ExitSurvey_Confirm', + }, }, SAVE_THE_WORLD: { ROOT: 'SaveTheWorld_Root', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 89365d1e0224..286ccc07a615 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -241,6 +241,9 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: () => require('../../../pages/settings/Wallet/ReportCardLostPage').default as React.ComponentType, [SCREENS.KEYBOARD_SHORTCUTS]: () => require('../../../pages/KeyboardShortcutsPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType, }); const EnablePaymentsStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({ diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 1a495e92eb80..28955156da05 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -274,6 +274,15 @@ const linkingConfig: LinkingOptions<RootStackParamList> = { [SCREENS.KEYBOARD_SHORTCUTS]: { path: ROUTES.KEYBOARD_SHORTCUTS, }, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_REASON.route, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.route, + }, }, }, [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: { diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx new file mode 100644 index 000000000000..ff4e39113581 --- /dev/null +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import Button from '@components/Button'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import Navigation from '@navigation/Navigation'; +import * as Link from '@userActions/Link'; +import CONST from '@src/CONST'; + +function ExitSurveyReasonPage() { + return ( + <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> + <HeaderWithBackButton + title="Before you go" + onBackButtonPress={() => Navigation.goBack()} + /> + <Text>Confirm page</Text> + <Button + text="Next" + onPress={() => { + Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + }} + /> + </ScreenWrapper> + ); +} + +ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; + +export default ExitSurveyReasonPage; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx new file mode 100644 index 000000000000..8d6d9289a78d --- /dev/null +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Button from '@components/Button'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import Navigation from '@navigation/Navigation'; +import ROUTES from '@src/ROUTES'; + +function ExitSurveyReasonPage() { + return ( + <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> + <HeaderWithBackButton + title="Before you go" + onBackButtonPress={() => Navigation.goBack()} + /> + <Text>Reason page</Text> + <Button + text="Next" + onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(ROUTES.SETTINGS))} + /> + </ScreenWrapper> + ); +} + +ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; + +export default ExitSurveyReasonPage; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx new file mode 100644 index 000000000000..f30056b11752 --- /dev/null +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Button from '@components/Button'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import Text from '@components/Text'; +import Navigation from '@navigation/Navigation'; +import ROUTES from '@src/ROUTES'; + +function ExitSurveyReasonPage() { + return ( + <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> + <HeaderWithBackButton + title="Before you go" + onBackButtonPress={() => Navigation.goBack()} + /> + <Text>Response page</Text> + <Button + text="Next" + onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.getRoute(ROUTES.SETTINGS_EXIT_SURVEY_REASON.route))} + /> + </ScreenWrapper> + ); +} + +ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; + +export default ExitSurveyReasonPage; diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 6e310b9a62bd..239f41321862 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -267,11 +267,8 @@ function InitialSettingsPage(props) { translationKey: 'initialSettingsPage.goToExpensifyClassic', icon: Expensicons.NewExpensify, action: () => { - Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON.route); }, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, - link: Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), }, { translationKey: 'initialSettingsPage.signOut', From 835c2da71ce6751f5d267d69450424b457f8787e Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 15:27:08 -0800 Subject: [PATCH 03/81] Build basic layout for reason page --- src/ONYXKEYS.ts | 2 + .../ExitSurvey/ExitSurveyReasonPage.tsx | 61 ++++++++++++++++--- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 40a43d8195de..1ae900283002 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -350,6 +350,8 @@ const ONYXKEYS = { REPORT_VIRTUAL_CARD_FRAUD_DRAFT: 'reportVirtualCardFraudFormDraft', GET_PHYSICAL_CARD_FORM: 'getPhysicalCardForm', GET_PHYSICAL_CARD_FORM_DRAFT: 'getPhysicalCardFormDraft', + EXIT_SURVEY_REASON_FORM: 'exitSurveyReasonForm', + EXIT_SURVEY_RESPONSE_FORM: 'exitSurveyResponseForm', }, } as const; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 8d6d9289a78d..057c24892393 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,23 +1,70 @@ -import React from 'react'; -import Button from '@components/Button'; +import React, {useMemo, useState} from 'react'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import type {Choice} from '@components/RadioButtons'; +import RadioButtons from '@components/RadioButtons'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; function ExitSurveyReasonPage() { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + + // FIXME: use the reason! + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [reason, setReason] = useState<string>(); + + const reasons: Choice[] = useMemo( + () => [ + { + label: 'Choice A', + value: 'choiceA', + }, + { + label: 'Choice B', + value: 'choiceB', + }, + { + label: 'Choice C', + value: 'choiceC', + }, + ], + [], + ); + return ( <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> <HeaderWithBackButton title="Before you go" onBackButtonPress={() => Navigation.goBack()} /> - <Text>Reason page</Text> - <Button - text="Next" - onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(ROUTES.SETTINGS))} - /> + {/* @ts-expect-error - FormProvider is not yet migrated to TS */} + <FormProvider + formID={ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM} + style={[styles.flex1, styles.mt3, styles.mh5]} + // TODO: validation? + validate={() => {}} + onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(ROUTES.SETTINGS))} + submitButtonText={translate('common.next')} + shouldValidateOnBlur + shouldValidateOnChange + > + <Text style={styles.headerAnonymousFooter}>Please tell us why you're leaving</Text> + <Text style={styles.mt2}>Before you go, please tell us why you'd like to switch to Expensify Classic</Text> + <InputWrapper + // @ts-expect-error - InputWrapper is not yet migrated to TS + InputComponent={RadioButtons} + inputID="reason" + items={reasons} + onPress={(value: string) => setReason(value)} + /> + </FormProvider> </ScreenWrapper> ); } From e77baf791e448c65c1c68af2dcc714714fc2036f Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 15:38:10 -0800 Subject: [PATCH 04/81] Add translations for copy --- src/languages/en.ts | 19 +++++++++++++++++++ src/languages/es.ts | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index b6da38df21a0..17f01c5a7c83 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2119,4 +2119,23 @@ export default { taxRateChanged: 'Tax rate was modified', taxRequired: 'Missing tax rate', }, + exitSurvey: { + header: 'Before you go', + title: 'Before you go, please tell us why you’d like to switch to Expensify Classic.', + reasons: { + // TODO: use consts for these keys + featureNotAvailable: "I need a feature that's only available in Expensify Classic.", + dontUnderstand: "I don't understand how to use New Expensify.", + preferClassic: 'I understand how to use New Expensify, but I prefer Expensify Classic.', + }, + prompts: { + featureNotAvailable: "What feature do you need that isn't available in New Expensify?", + dontUnderstand: 'What are you trying to do?', + preferClassic: 'Why to you prefer Classic Expensify?', + }, + thankYou: 'Your responses will help us build a better product to get stuff done. Thank you so much!', + goToExpensifyClassic: 'Switch to Expensify Classic', + offline: + "You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.", + }, } satisfies TranslationBase; diff --git a/src/languages/es.ts b/src/languages/es.ts index 2478c8ba8bd2..1b7011a3310b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2608,4 +2608,23 @@ export default { taxRateChanged: 'La tasa de impuesto fue modificada', taxRequired: 'Falta tasa de impuesto', }, + exitSurvey: { + header: 'Antes de irte', + title: 'Antes de irte, por favor dinos por qué te gustaría cambiarte a Expensify Classic.', + reasons: { + // TODO: use consts for these keys + featureNotAvailable: 'Necesito una función que sólo está disponible en Expensify Classic.', + dontUnderstand: 'No entiendo cómo usar New Expensify.', + preferClassic: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', + }, + prompts: { + featureNotAvailable: '¿Qué función necesitas que no esté disponible en New Expensify?', + dontUnderstand: '¿Qué estás tratando de hacer?', + preferClassic: '¿Por qué prefieres Expensify Classic?', + }, + thankYou: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', + goToExpensifyClassic: 'Cambiar a Expensify Classic', + offline: + 'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.', + }, } satisfies EnglishTranslation; From a566335f946d3dcbde78b79fe3eb7583f5f2fac4 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 15:41:52 -0800 Subject: [PATCH 05/81] Add constants for reasons --- src/CONST.ts | 6 ++++++ src/languages/en.ts | 12 ++++++------ src/languages/es.ts | 12 ++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index accce6e92555..186fafaf4f22 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3131,6 +3131,12 @@ const CONST = { }, MINI_CONTEXT_MENU_MAX_ITEMS: 4, + + EXIT_SURVEY_REASONS: { + FEATURE_NOT_AVAILABLE: 'featureNotAvailable', + DONT_UNDERSTAND: 'dontUnderstand', + PREFER_CLASSIC: 'preferClassic', + }, } as const; export default CONST; diff --git a/src/languages/en.ts b/src/languages/en.ts index 17f01c5a7c83..f2881a70551d 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2124,14 +2124,14 @@ export default { title: 'Before you go, please tell us why you’d like to switch to Expensify Classic.', reasons: { // TODO: use consts for these keys - featureNotAvailable: "I need a feature that's only available in Expensify Classic.", - dontUnderstand: "I don't understand how to use New Expensify.", - preferClassic: 'I understand how to use New Expensify, but I prefer Expensify Classic.', + [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "I need a feature that's only available in Expensify Classic.", + [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: "I don't understand how to use New Expensify.", + [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'I understand how to use New Expensify, but I prefer Expensify Classic.', }, prompts: { - featureNotAvailable: "What feature do you need that isn't available in New Expensify?", - dontUnderstand: 'What are you trying to do?', - preferClassic: 'Why to you prefer Classic Expensify?', + [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "What feature do you need that isn't available in New Expensify?", + [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: 'What are you trying to do?', + [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Why to you prefer Classic Expensify?', }, thankYou: 'Your responses will help us build a better product to get stuff done. Thank you so much!', goToExpensifyClassic: 'Switch to Expensify Classic', diff --git a/src/languages/es.ts b/src/languages/es.ts index 1b7011a3310b..6e20ede364ea 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2613,14 +2613,14 @@ export default { title: 'Antes de irte, por favor dinos por qué te gustaría cambiarte a Expensify Classic.', reasons: { // TODO: use consts for these keys - featureNotAvailable: 'Necesito una función que sólo está disponible en Expensify Classic.', - dontUnderstand: 'No entiendo cómo usar New Expensify.', - preferClassic: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', + [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: 'Necesito una función que sólo está disponible en Expensify Classic.', + [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: 'No entiendo cómo usar New Expensify.', + [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', }, prompts: { - featureNotAvailable: '¿Qué función necesitas que no esté disponible en New Expensify?', - dontUnderstand: '¿Qué estás tratando de hacer?', - preferClassic: '¿Por qué prefieres Expensify Classic?', + [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: '¿Qué función necesitas que no esté disponible en New Expensify?', + [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: '¿Qué estás tratando de hacer?', + [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', }, thankYou: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', goToExpensifyClassic: 'Cambiar a Expensify Classic', From d043c79a5644cad69b5bf8946fe5ca6979ce1f11 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 16:14:54 -0800 Subject: [PATCH 06/81] Use correct reasons from CONST in reasons page --- src/ROUTES.ts | 2 +- src/libs/Navigation/types.ts | 5 +++ .../ExitSurvey/ExitSurveyReasonPage.tsx | 37 ++++++++----------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b59348e5edf9..02036e4824d3 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -150,7 +150,7 @@ const ROUTES = { }, SETTINGS_EXIT_SURVEY_RESPONSE: { route: 'settings/exit-survey/response', - getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/response', backTo), + getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>, backTo?: string) => getUrlWithBackToParam(`settings/exit-survey/response&reason=${encodeURIComponent(reason)}`, backTo), }, SETTINGS_EXIT_SURVEY_CONFIRM: { route: 'settings/exit-survey/confirm', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index a1cbb562997e..6da0bc36d7e0 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -124,6 +124,11 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: undefined; [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: undefined; [SCREENS.KEYBOARD_SHORTCUTS]: undefined; + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => undefined; + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => { + reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>; + }; + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => undefined; }; type NewChatNavigatorParamList = { diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 057c24892393..a50920e4b994 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,4 +1,5 @@ import React, {useMemo, useState} from 'react'; +import type {ValueOf} from 'type-fest'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -9,6 +10,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -16,26 +18,14 @@ function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - // FIXME: use the reason! - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [reason, setReason] = useState<string>(); - + const [reason, setReason] = useState<ValueOf<typeof CONST.EXIT_SURVEY_REASONS>>(); const reasons: Choice[] = useMemo( - () => [ - { - label: 'Choice A', - value: 'choiceA', - }, - { - label: 'Choice B', - value: 'choiceB', - }, - { - label: 'Choice C', - value: 'choiceC', - }, - ], - [], + () => + Object.values(CONST.EXIT_SURVEY_REASONS).map((value) => ({ + value, + label: translate(`exitSurvey.reasons.${value}`), + })), + [translate], ); return ( @@ -50,7 +40,12 @@ function ExitSurveyReasonPage() { style={[styles.flex1, styles.mt3, styles.mh5]} // TODO: validation? validate={() => {}} - onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(ROUTES.SETTINGS))} + onSubmit={() => { + if (!reason) { + return; + } + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS)); + }} submitButtonText={translate('common.next')} shouldValidateOnBlur shouldValidateOnChange @@ -62,7 +57,7 @@ function ExitSurveyReasonPage() { InputComponent={RadioButtons} inputID="reason" items={reasons} - onPress={(value: string) => setReason(value)} + onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => setReason(value)} /> </FormProvider> </ScreenWrapper> From f5e2ded82a43357c5c013d0fb7356f3658159299 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 16:31:18 -0800 Subject: [PATCH 07/81] Fix radio button styles --- src/components/RadioButtonWithLabel.tsx | 2 +- src/components/RadioButtons.tsx | 4 +++- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/RadioButtonWithLabel.tsx b/src/components/RadioButtonWithLabel.tsx index f4bad4c082a7..03dbda4417ef 100644 --- a/src/components/RadioButtonWithLabel.tsx +++ b/src/components/RadioButtonWithLabel.tsx @@ -54,7 +54,7 @@ function RadioButtonWithLabel({LabelComponent, style, label = '', hasError = fal accessible={false} onPress={onPress} style={[styles.flexRow, styles.flexWrap, styles.flexShrink1, styles.alignItemsCenter]} - wrapperStyle={[styles.ml3, styles.pr2, styles.w100]} + wrapperStyle={[styles.flex1, styles.ml3, styles.pr2]} // disable hover style when disabled hoverDimmingValue={0.8} pressDimmingValue={0.5} diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index cb6843af65c0..2f7765484c19 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -1,11 +1,13 @@ import React, {useState} from 'react'; import {View} from 'react-native'; +import type {StyleProp, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import RadioButtonWithLabel from './RadioButtonWithLabel'; type Choice = { label: string; value: string; + style?: StyleProp<ViewStyle>; }; type RadioButtonsProps = { @@ -26,7 +28,7 @@ function RadioButtons({items, onPress}: RadioButtonsProps) { <RadioButtonWithLabel key={item.value} isChecked={item.value === checkedValue} - style={styles.mt4} + style={[styles.mt4, item.style]} onPress={() => { setCheckedValue(item.value); return onPress(item.value); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index a50920e4b994..d93b0d8d6a66 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -24,8 +24,9 @@ function ExitSurveyReasonPage() { Object.values(CONST.EXIT_SURVEY_REASONS).map((value) => ({ value, label: translate(`exitSurvey.reasons.${value}`), + style: styles.mt6, })), - [translate], + [styles, translate], ); return ( From 1f1b22fed466b9291f0e3c4117520e33a110ca93 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 16:57:02 -0800 Subject: [PATCH 08/81] Implement validation in reason page --- src/components/RadioButtons.tsx | 38 +++++++++++-------- src/components/SingleChoiceQuestion.tsx | 3 +- .../ExitSurvey/ExitSurveyReasonPage.tsx | 14 +++++-- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 2f7765484c19..0055f23ca29d 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -2,6 +2,8 @@ import React, {useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; +import type {MaybePhraseKey} from '@libs/Localize'; +import FormHelpMessage from './FormHelpMessage'; import RadioButtonWithLabel from './RadioButtonWithLabel'; type Choice = { @@ -16,27 +18,33 @@ type RadioButtonsProps = { /** Callback to fire when selecting a radio button */ onPress: (value: string) => void; + + /** Potential error text provided by a form InputWrapper */ + errorText?: MaybePhraseKey; }; -function RadioButtons({items, onPress}: RadioButtonsProps) { +function RadioButtons({items, onPress, errorText}: RadioButtonsProps) { const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(''); return ( - <View style={styles.mb3}> - {items.map((item) => ( - <RadioButtonWithLabel - key={item.value} - isChecked={item.value === checkedValue} - style={[styles.mt4, item.style]} - onPress={() => { - setCheckedValue(item.value); - return onPress(item.value); - }} - label={item.label} - /> - ))} - </View> + <> + <View style={styles.mb3}> + {items.map((item) => ( + <RadioButtonWithLabel + key={item.value} + isChecked={item.value === checkedValue} + style={[styles.mt4, item.style]} + onPress={() => { + setCheckedValue(item.value); + return onPress(item.value); + }} + label={item.label} + /> + ))} + </View> + {!!errorText && <FormHelpMessage message={errorText} />} + </> ); } diff --git a/src/components/SingleChoiceQuestion.tsx b/src/components/SingleChoiceQuestion.tsx index c8bf783032ad..3ff844dd80e9 100644 --- a/src/components/SingleChoiceQuestion.tsx +++ b/src/components/SingleChoiceQuestion.tsx @@ -4,7 +4,6 @@ import React, {forwardRef} from 'react'; import type {Text as RNText} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; import type {MaybePhraseKey} from '@libs/Localize'; -import FormHelpMessage from './FormHelpMessage'; import type {Choice} from './RadioButtons'; import RadioButtons from './RadioButtons'; import Text from './Text'; @@ -32,8 +31,8 @@ function SingleChoiceQuestion({prompt, errorText, possibleAnswers, currentQuesti items={possibleAnswers} key={currentQuestionIndex} onPress={onInputChange} + errorText={errorText} /> - <FormHelpMessage message={errorText} /> </> ); } diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index d93b0d8d6a66..cfd1ccf1ae33 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -14,6 +14,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +const REASON_INPUT_ID = 'reason'; + function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); @@ -39,8 +41,14 @@ function ExitSurveyReasonPage() { <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM} style={[styles.flex1, styles.mt3, styles.mh5]} - // TODO: validation? - validate={() => {}} + validate={() => { + if (reason) { + return {}; + } + return { + [REASON_INPUT_ID]: translate('common.error.fieldRequired'), + }; + }} onSubmit={() => { if (!reason) { return; @@ -56,7 +64,7 @@ function ExitSurveyReasonPage() { <InputWrapper // @ts-expect-error - InputWrapper is not yet migrated to TS InputComponent={RadioButtons} - inputID="reason" + inputID={REASON_INPUT_ID} items={reasons} onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => setReason(value)} /> From 6ccfdd802af41bfb44d78c61c6106c9b7ca34495 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:03:11 -0800 Subject: [PATCH 09/81] Simplify routes --- src/ROUTES.ts | 12 +++--------- src/libs/Navigation/linkingConfig.ts | 4 ++-- .../settings/ExitSurvey/ExitSurveyReasonPage.tsx | 2 +- .../settings/ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- src/pages/settings/InitialSettingsPage.js | 2 +- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 02036e4824d3..c003c3340951 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -144,18 +144,12 @@ const ROUTES = { SETTINGS_STATUS_CLEAR_AFTER_DATE: 'settings/profile/status/clear-after/date', SETTINGS_STATUS_CLEAR_AFTER_TIME: 'settings/profile/status/clear-after/time', - SETTINGS_EXIT_SURVEY_REASON: { - route: 'settings/exit-survey/reason', - getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/reason', backTo), - }, + SETTINGS_EXIT_SURVEY_REASON: 'settings/exit-survey/reason', SETTINGS_EXIT_SURVEY_RESPONSE: { route: 'settings/exit-survey/response', - getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>, backTo?: string) => getUrlWithBackToParam(`settings/exit-survey/response&reason=${encodeURIComponent(reason)}`, backTo), - }, - SETTINGS_EXIT_SURVEY_CONFIRM: { - route: 'settings/exit-survey/confirm', - getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/confirm', backTo), + getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => `settings/exit-survey/response?reason=${encodeURIComponent(reason)}` as const, }, + SETTINGS_EXIT_SURVEY_CONFIRM: 'settings/exit-survey/confirm', KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', diff --git a/src/libs/Navigation/linkingConfig.ts b/src/libs/Navigation/linkingConfig.ts index 28955156da05..bbb7b58a3121 100644 --- a/src/libs/Navigation/linkingConfig.ts +++ b/src/libs/Navigation/linkingConfig.ts @@ -275,13 +275,13 @@ const linkingConfig: LinkingOptions<RootStackParamList> = { path: ROUTES.KEYBOARD_SHORTCUTS, }, [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_REASON.route, + path: ROUTES.SETTINGS_EXIT_SURVEY_REASON, }, [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, }, [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.route, + path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM, }, }, }, diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index cfd1ccf1ae33..2e1be39baa9f 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -53,7 +53,7 @@ function ExitSurveyReasonPage() { if (!reason) { return; } - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS)); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason)); }} submitButtonText={translate('common.next')} shouldValidateOnBlur diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index f30056b11752..4cb40b935c5d 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -16,7 +16,7 @@ function ExitSurveyReasonPage() { <Text>Response page</Text> <Button text="Next" - onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.getRoute(ROUTES.SETTINGS_EXIT_SURVEY_REASON.route))} + onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} /> </ScreenWrapper> ); diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 239f41321862..66d2a2b820e5 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -267,7 +267,7 @@ function InitialSettingsPage(props) { translationKey: 'initialSettingsPage.goToExpensifyClassic', icon: Expensicons.NewExpensify, action: () => { - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON.route); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); }, }, { From 344bd5eb2c17a26aacb7b92ef7e249d5e95d5d26 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:10:54 -0800 Subject: [PATCH 10/81] Fix skeletons of response and confirm page --- .../settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 8 ++++---- .../settings/ExitSurvey/ExitSurveyResponsePage.tsx | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index ff4e39113581..a0fe2aa845d6 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -7,9 +7,9 @@ import Navigation from '@navigation/Navigation'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; -function ExitSurveyReasonPage() { +function ExitSurveyConfirmPage() { return ( - <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> + <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton title="Before you go" onBackButtonPress={() => Navigation.goBack()} @@ -25,6 +25,6 @@ function ExitSurveyReasonPage() { ); } -ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; +ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; -export default ExitSurveyReasonPage; +export default ExitSurveyConfirmPage; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 4cb40b935c5d..2fbdafdc97dc 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -3,12 +3,15 @@ import Button from '@components/Button'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@navigation/Navigation'; import ROUTES from '@src/ROUTES'; -function ExitSurveyReasonPage() { +function ExitSurveyResponsePage() { + const {translate} = useLocalize(); + return ( - <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> + <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton title="Before you go" onBackButtonPress={() => Navigation.goBack()} @@ -22,6 +25,6 @@ function ExitSurveyReasonPage() { ); } -ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; +ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; -export default ExitSurveyReasonPage; +export default ExitSurveyResponsePage; From 138e14c4cc3de9d80da91d8c00b4359161104e45 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:12:28 -0800 Subject: [PATCH 11/81] Localize page headers --- src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 5 ++++- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 2 +- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index a0fe2aa845d6..a7d2ebad8214 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -3,15 +3,18 @@ import Button from '@components/Button'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@navigation/Navigation'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; function ExitSurveyConfirmPage() { + const {translate} = useLocalize(); + return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton - title="Before you go" + title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> <Text>Confirm page</Text> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 2e1be39baa9f..efc9c5ffa150 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -34,7 +34,7 @@ function ExitSurveyReasonPage() { return ( <ScreenWrapper testID={ExitSurveyReasonPage.displayName}> <HeaderWithBackButton - title="Before you go" + title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> {/* @ts-expect-error - FormProvider is not yet migrated to TS */} diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 2fbdafdc97dc..7dda7a997dbb 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -13,7 +13,7 @@ function ExitSurveyResponsePage() { return ( <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton - title="Before you go" + title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> <Text>Response page</Text> From 19991afc128d993f8aa94187fe729cd6f32ba1ae Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:30:18 -0800 Subject: [PATCH 12/81] Translate title and subtitle on reason page --- src/languages/en.ts | 5 ++++- src/languages/es.ts | 5 ++++- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index f2881a70551d..1242c02a99b1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2121,7 +2121,10 @@ export default { }, exitSurvey: { header: 'Before you go', - title: 'Before you go, please tell us why you’d like to switch to Expensify Classic.', + reasonPage: { + title: "Please tell us why you're leaving", + subtitle: 'Before you go, please tell us why you’d like to switch to Expensify Classic.', + }, reasons: { // TODO: use consts for these keys [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "I need a feature that's only available in Expensify Classic.", diff --git a/src/languages/es.ts b/src/languages/es.ts index 6e20ede364ea..722a939c1e93 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2610,7 +2610,10 @@ export default { }, exitSurvey: { header: 'Antes de irte', - title: 'Antes de irte, por favor dinos por qué te gustaría cambiarte a Expensify Classic.', + reasonPage: { + title: 'Dinos por qué te vas', + subtitle: 'Antes de irte, por favor dinos por qué te gustaría cambiarte a Expensify Classic.', + }, reasons: { // TODO: use consts for these keys [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: 'Necesito una función que sólo está disponible en Expensify Classic.', diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index efc9c5ffa150..7f135f5bd347 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -59,8 +59,8 @@ function ExitSurveyReasonPage() { shouldValidateOnBlur shouldValidateOnChange > - <Text style={styles.headerAnonymousFooter}>Please tell us why you're leaving</Text> - <Text style={styles.mt2}>Before you go, please tell us why you'd like to switch to Expensify Classic</Text> + <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> + <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> <InputWrapper // @ts-expect-error - InputWrapper is not yet migrated to TS InputComponent={RadioButtons} From d76486934197f489003cf6e1534249be227dcaa6 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:33:20 -0800 Subject: [PATCH 13/81] Implement basic layout of ResponsePage --- src/languages/en.ts | 3 +- src/languages/es.ts | 1 + src/libs/Navigation/types.ts | 6 +-- .../ExitSurvey/ExitSurveyResponsePage.tsx | 39 +++++++++++++++---- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 1242c02a99b1..7c763dcbd57b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2134,8 +2134,9 @@ export default { prompts: { [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "What feature do you need that isn't available in New Expensify?", [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: 'What are you trying to do?', - [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Why to you prefer Classic Expensify?', + [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Why to you prefer Expensify Classic?', }, + responsePlaceholder: 'Your response', thankYou: 'Your responses will help us build a better product to get stuff done. Thank you so much!', goToExpensifyClassic: 'Switch to Expensify Classic', offline: diff --git a/src/languages/es.ts b/src/languages/es.ts index 722a939c1e93..e2f2abd4cedf 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2625,6 +2625,7 @@ export default { [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: '¿Qué estás tratando de hacer?', [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', }, + responsePlaceholder: 'Su respuesta', thankYou: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', goToExpensifyClassic: 'Cambiar a Expensify Classic', offline: diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 6da0bc36d7e0..47f53cb6647b 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -124,11 +124,11 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: undefined; [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: undefined; [SCREENS.KEYBOARD_SHORTCUTS]: undefined; - [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => undefined; - [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => { + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>; }; - [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => undefined; + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: undefined; }; type NewChatNavigatorParamList = { diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 7dda7a997dbb..a87b0365ba39 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,14 +1,27 @@ +import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; -import Button from '@components/Button'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; +import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; -function ExitSurveyResponsePage() { +const RESPONSE_INPUT_ID = 'response'; + +type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; + +function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); + const styles = useThemeStyles(); + const {reason} = route.params; return ( <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> @@ -16,11 +29,23 @@ function ExitSurveyResponsePage() { title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> - <Text>Response page</Text> - <Button - text="Next" - onPress={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} - /> + <FormProvider + formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} + style={[styles.flex1, styles.mt3, styles.mh5]} + onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} + submitButtonText={translate('common.next')} + shouldValidateOnBlur + shouldValidateOnChange + > + <Text style={styles.headerAnonymousFooter}>{translate(`exitSurvey.prompts.${reason}`)}</Text> + <InputWrapper + InputComponent={TextInput} + inputID={RESPONSE_INPUT_ID} + containerStyles={styles.mt7} + placeholder={translate(`exitSurvey.responsePlaceholder`)} + multiline + /> + </FormProvider> </ScreenWrapper> ); } From 8cceceae99306d89d4112806cb8d4f0106c559b9 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 17:46:12 -0800 Subject: [PATCH 14/81] Style response input better --- .../ExitSurvey/ExitSurveyReasonPage.tsx | 20 +++++++++------- .../ExitSurvey/ExitSurveyResponsePage.tsx | 24 ++++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 7f135f5bd347..7de412a3a581 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -59,15 +59,17 @@ function ExitSurveyReasonPage() { shouldValidateOnBlur shouldValidateOnChange > - <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> - <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> - <InputWrapper - // @ts-expect-error - InputWrapper is not yet migrated to TS - InputComponent={RadioButtons} - inputID={REASON_INPUT_ID} - items={reasons} - onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => setReason(value)} - /> + <> + <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> + <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> + <InputWrapper + // @ts-expect-error – InputWrapper is not yet implemented in TS + InputComponent={RadioButtons} + inputID={REASON_INPUT_ID} + items={reasons} + onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => setReason(value)} + /> + </> </FormProvider> </ScreenWrapper> ); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index a87b0365ba39..08754d7601bc 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -10,6 +10,7 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; @@ -29,6 +30,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> + {/* @ts-expect-error - FormProvider is not yet migrated to TS */} <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} style={[styles.flex1, styles.mt3, styles.mh5]} @@ -37,14 +39,20 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { shouldValidateOnBlur shouldValidateOnChange > - <Text style={styles.headerAnonymousFooter}>{translate(`exitSurvey.prompts.${reason}`)}</Text> - <InputWrapper - InputComponent={TextInput} - inputID={RESPONSE_INPUT_ID} - containerStyles={styles.mt7} - placeholder={translate(`exitSurvey.responsePlaceholder`)} - multiline - /> + <> + <Text style={styles.headerAnonymousFooter}>{translate(`exitSurvey.prompts.${reason}`)}</Text> + <InputWrapper + // @ts-expect-error – InputWrapper is not yet implemented in TS + InputComponent={TextInput} + inputID={RESPONSE_INPUT_ID} + containerStyles={styles.mt7} + label={translate(`exitSurvey.responsePlaceholder`)} + accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} + multiline + role={CONST.ROLE.PRESENTATION} + shouldSaveDraft + /> + </> </FormProvider> </ScreenWrapper> ); From 1063d50c1b5bc4e63ea7c7dab049035966fb6bf2 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 22 Jan 2024 18:02:20 -0800 Subject: [PATCH 15/81] Get multiline styles implemented correctly --- .../ExitSurvey/ExitSurveyResponsePage.tsx | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 08754d7601bc..e8a153fcfe0b 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; +import React, {useRef, useState} from 'react'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -8,6 +8,7 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import CONST from '@src/CONST'; @@ -22,8 +23,12 @@ type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const {reason} = route.params; + const [response, setResponse] = useState(''); + const responseInputRef = useRef(null); + return ( <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton @@ -45,11 +50,21 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={TextInput} inputID={RESPONSE_INPUT_ID} - containerStyles={styles.mt7} label={translate(`exitSurvey.responsePlaceholder`)} accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} - multiline role={CONST.ROLE.PRESENTATION} + autoGrowHeight + maxLength={CONST.MAX_COMMENT_LENGTH} + ref={(el) => { + if (!el) { + return; + } + responseInputRef.current = el; + updateMultilineInputRange(el); + }} + value={response} + onChangeText={setResponse} + containerStyles={[styles.mt7, styles.autoGrowHeightMultilineInput]} shouldSaveDraft /> </> From 0f1fc6859df6109817858cf925e9d4b178fa02c5 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 23 Jan 2024 14:50:30 -0800 Subject: [PATCH 16/81] Compute maxHeight for TextInput in component --- .../ExitSurvey/ExitSurveyResponsePage.tsx | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index e8a153fcfe0b..1f7863e807da 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useRef, useState} from 'react'; +import {useSafeAreaFrame} from 'react-native-safe-area-context'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -7,10 +8,12 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; +import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -23,12 +26,33 @@ type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + const {height: safeAreaFrameHeight} = useSafeAreaFrame(); const {reason} = route.params; const [response, setResponse] = useState(''); const responseInputRef = useRef(null); + const formTopPaddingStyle = styles.mt3; + const textStyle = styles.headerAnonymousFooter; + const baseResponseInputContainerStyle = styles.mt7; + const responseInputMaxHeight = Math.floor( + // approximation for window height minus safe areas + safeAreaFrameHeight - + // Minus the height of HeaderWithBackButton + variables.contentHeaderHeight - + // Minus the top padding on the form + formTopPaddingStyle.marginTop - + // Minus the height of the text component + textStyle.lineHeight - + // Minus the response input margins (multiplied by 2 to create the effect of margins on top and bottom). + // marginBottom does not work in this case because the TextInput is in a ScrollView and will push the button beneath it out of view, + // so it's maxHeight is what dictates space between it and the button. + baseResponseInputContainerStyle.marginTop * 2 - + // Minus the approximate size of a default button + variables.componentSizeLarge, + ); return ( <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton @@ -38,14 +62,14 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { {/* @ts-expect-error - FormProvider is not yet migrated to TS */} <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} - style={[styles.flex1, styles.mt3, styles.mh5]} + style={[styles.flex1, styles.mh5, formTopPaddingStyle]} onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} submitButtonText={translate('common.next')} shouldValidateOnBlur shouldValidateOnChange > <> - <Text style={styles.headerAnonymousFooter}>{translate(`exitSurvey.prompts.${reason}`)}</Text> + <Text style={textStyle}>{translate(`exitSurvey.prompts.${reason}`)}</Text> <InputWrapper // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={TextInput} @@ -64,7 +88,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { }} value={response} onChangeText={setResponse} - containerStyles={[styles.mt7, styles.autoGrowHeightMultilineInput]} + containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} shouldSaveDraft /> </> From 14351a7e6666915ecb37afbbffc9243865e4fd4f Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 23 Jan 2024 15:21:41 -0800 Subject: [PATCH 17/81] Polish response input height --- src/libs/NumberUtils.ts | 11 +++++-- .../ExitSurvey/ExitSurveyResponsePage.tsx | 32 +++++++++++++------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/libs/NumberUtils.ts b/src/libs/NumberUtils.ts index ddbd42243758..b4574afde51f 100644 --- a/src/libs/NumberUtils.ts +++ b/src/libs/NumberUtils.ts @@ -50,7 +50,7 @@ function generateHexadecimalValue(num: number): string { /** * Clamp a number in a range. * This is a worklet so it should be used only from UI thread. - + * @returns clamped value between min and max */ function clampWorklet(num: number, min: number, max: number): number { @@ -81,4 +81,11 @@ function parseFloatAnyLocale(value: string): number { return parseFloat(value ? value.replace(',', '.') : value); } -export {rand64, generateHexadecimalValue, generateRandomInt, clampWorklet, parseFloatAnyLocale}; +/** + * Given an input number q and another number n, returns the largest number x that's less than p and divisible by q. + */ +function roundDownToLargestMultiple(p: number, q: number) { + return Math.floor(p / q) * q; +} + +export {rand64, generateHexadecimalValue, generateRandomInt, clampWorklet, parseFloatAnyLocale, roundDownToLargestMultiple}; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 1f7863e807da..1c5bc163b593 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,6 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useRef, useState} from 'react'; -import {useSafeAreaFrame} from 'react-native-safe-area-context'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -8,8 +7,11 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; +import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as NumberUtils from '@libs/NumberUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -27,23 +29,27 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {height: safeAreaFrameHeight} = useSafeAreaFrame(); + const {windowHeight} = useWindowDimensions(); + const {top: safeAreaInsetsTop} = useSafeAreaInsets(); const {reason} = route.params; const [response, setResponse] = useState(''); const responseInputRef = useRef(null); - const formTopPaddingStyle = styles.mt3; + const formTopMarginsStyle = styles.mt3; const textStyle = styles.headerAnonymousFooter; const baseResponseInputContainerStyle = styles.mt7; - const responseInputMaxHeight = Math.floor( - // approximation for window height minus safe areas - safeAreaFrameHeight - + const formMaxHeight = Math.floor( + windowHeight - + safeAreaInsetsTop - // Minus the height of HeaderWithBackButton variables.contentHeaderHeight - - // Minus the top padding on the form - formTopPaddingStyle.marginTop - + // Minus the top margins on the form + formTopMarginsStyle.marginTop, + ); + const responseInputMaxHeight = NumberUtils.roundDownToLargestMultiple( + formMaxHeight - // Minus the height of the text component textStyle.lineHeight - // Minus the response input margins (multiplied by 2 to create the effect of margins on top and bottom). @@ -51,8 +57,14 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { // so it's maxHeight is what dictates space between it and the button. baseResponseInputContainerStyle.marginTop * 2 - // Minus the approximate size of a default button - variables.componentSizeLarge, + variables.componentSizeLarge - + // Minus the vertical margins around the form button + 40, + + // Round down to the largest number of full lines + styles.baseTextInput.lineHeight, ); + return ( <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton @@ -62,7 +74,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { {/* @ts-expect-error - FormProvider is not yet migrated to TS */} <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} - style={[styles.flex1, styles.mh5, formTopPaddingStyle]} + style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} submitButtonText={translate('common.next')} shouldValidateOnBlur From af1110f9627918d2784de0cbb64e6cb636ea902f Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 18:50:22 -0800 Subject: [PATCH 18/81] Make response required --- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 1c5bc163b593..2e9d5cc4ab26 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -77,6 +77,14 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} submitButtonText={translate('common.next')} + validate={() => { + if (response?.trim()) { + return {}; + } + return { + [RESPONSE_INPUT_ID]: translate('common.error.fieldRequired'), + }; + }} shouldValidateOnBlur shouldValidateOnChange > From 21d0449f2267cc9a7e51ffd52a99a67f1a41f475 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 18:55:48 -0800 Subject: [PATCH 19/81] Add mushroom-top-hat illustration asset --- .../mushroom-top-hat.svg | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 assets/images/product-illustrations/mushroom-top-hat.svg diff --git a/assets/images/product-illustrations/mushroom-top-hat.svg b/assets/images/product-illustrations/mushroom-top-hat.svg new file mode 100644 index 000000000000..f09f7e7378ed --- /dev/null +++ b/assets/images/product-illustrations/mushroom-top-hat.svg @@ -0,0 +1,73 @@ +<svg width="136" height="128" viewBox="0 0 136 128" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g id="Illustration"> +<path id="Vector" d="M20.9596 119.21C20.9596 119.21 21.6229 120.98 23.2829 120.98C24.9429 120.98 27.3762 121.09 27.3762 121.09L28.9262 122.416C28.9262 122.416 30.1429 126.62 33.4595 126.176C36.7761 125.733 36.5561 123.19 36.5561 123.19C36.5561 123.19 39.4328 122.526 39.8728 120.203C39.8728 120.203 42.5261 119.76 42.7494 117.66C42.7494 117.66 47.2894 119.186 53.2026 116.266C59.1159 113.346 58.0126 107.043 58.0126 107.043C58.0126 107.043 55.5793 100.74 54.8059 100.187C54.0326 99.6332 51.8193 97.7533 51.8193 97.7533L21.9563 89.0167L31.1362 109.92C31.1362 109.92 17.753 110.803 20.9596 119.21Z" fill="#FFFAF0"/> +<path id="Vector_2" d="M4.70031 76.5168C4.70031 76.5168 4.36697 83.3734 15.7602 87.0234C27.1534 90.6733 49.9365 97.1999 49.9365 97.1999C49.9365 97.1999 66.6364 100.85 69.0697 92.0033C71.503 83.1567 67.1297 80.8901 63.873 78.8434C63.873 78.8434 49.1632 70.7702 33.9 67.2303C18.6368 63.6903 10.7836 63.2503 6.58362 70.1069C5.70029 72.5402 4.59364 75.5267 4.70364 76.5201L4.70031 76.5168Z" fill="#03D47C"/> +<path id="Vector_3" d="M27.2634 85.3667C30.868 85.3667 33.79 82.4448 33.79 78.8402C33.79 75.2357 30.868 72.3136 27.2634 72.3136C23.6589 72.3136 20.7368 75.2357 20.7368 78.8402C20.7368 82.4448 23.6589 85.3667 27.2634 85.3667Z" fill="#FFFAF0"/> +<path id="Vector_4" d="M40.5361 89.7899C44.1407 89.7899 47.0627 86.8678 47.0627 83.2633C47.0627 79.6587 44.1407 76.7368 40.5361 76.7368C36.9316 76.7368 34.0095 79.6587 34.0095 83.2633C34.0095 86.8678 36.9316 89.7899 40.5361 89.7899Z" fill="#FFFAF0"/> +<path id="Vector_5" d="M35.3361 52.187C35.3361 52.187 29.5129 46.5837 24.9396 53.2203C20.3696 59.8569 28.0362 65.3136 28.0362 65.3136L39.6861 69.0003L47.5027 68.4103C47.5027 68.4103 43.0794 65.3136 46.176 62.3636C49.2727 59.4137 51.336 65.167 55.1726 63.397C59.0059 61.627 58.5659 54.4004 54.2893 51.6004L52.666 52.7804L51.7826 7.36076C51.7826 7.36076 50.3093 0.577492 42.9361 1.02082C35.5628 1.46415 35.1195 5.44418 34.6795 9.42747C34.2362 13.4074 35.3428 52.1904 35.3428 52.1904L35.3361 52.187Z" fill="#F68DFE"/> +<path id="Vector_6" d="M35.3363 52.1869C35.3363 52.1869 35.363 52.207 35.4097 52.247C36.0463 52.7703 40.6796 56.477 44.0729 56.167C47.7229 55.8337 52.1462 52.737 52.1462 52.737L52.3662 47.7604C52.3662 47.7604 44.5129 45.217 35.333 47.6503V52.1837L35.3363 52.1869Z" fill="#E4BC07"/> +<path id="Vector_7" d="M79.5758 112.793L75.9259 123.19C75.9259 123.19 75.5925 125.07 78.8025 125.07H97.6057C97.6057 125.07 100.369 123.853 100.482 122.083C100.592 120.313 99.819 119.206 99.819 119.206L87.4324 113.233L79.5792 112.79L79.5758 112.793Z" fill="#FED607"/> +<path id="Vector_8" d="M121.492 111.356V119.1H132.662C132.662 119.1 134.762 117.883 134.762 115.12V98.64C134.762 98.64 133.879 95.3233 131.666 95.1C129.452 94.8766 128.236 96.87 128.236 96.87L121.489 111.36L121.492 111.356Z" fill="#FED607"/> +<path id="Vector_9" d="M23.54 94.5834L21.0334 89.1267L1.42023 111.1C1.42023 111.1 0.903572 114.86 1.64023 116.41C2.37689 117.96 5.77018 119.36 5.77018 119.36L20.5167 119.213C20.5167 119.213 19.1167 114.566 21.6234 112.873L8.86682 112.726L8.05683 112.283L23.54 94.5866V94.5834Z" fill="#002140"/> +<path id="Vector_10" d="M46.103 118.62H77.5127L79.4294 112.13L57.3096 112.276C57.3096 112.276 51.263 117.436 49.3463 117.66C47.4297 117.883 46.103 118.62 46.103 118.62Z" fill="#002140"/> +<path id="Vector_11" d="M87.4287 113.016L99.7053 119.543H120.938C120.938 119.543 121.712 113.35 120.828 111.8H87.5387L87.4287 113.016Z" fill="#002140"/> +<path id="Vector_12" d="M79.4293 112.13C79.4293 112.13 84.8859 114.01 87.4292 113.013L98.1558 84.3667C98.1558 84.3667 99.8157 78.1734 94.5058 76.8468C89.1958 75.5201 83.0026 83.0401 83.0026 83.0401L58.0061 109.473L57.3062 112.276L67.2961 112.24L85.8759 90.8933C85.8759 90.8933 87.0925 90.34 86.7592 92.11C86.4259 93.8799 79.4226 112.13 79.4226 112.13H79.4293Z" fill="#002140"/> +<path id="Vector_13" d="M76.3691 88.6834L75.7057 91.3367L80.7924 86.9134L82.3423 82.8201C82.3423 82.8201 80.4624 67.7802 62.5459 65.5669C62.5459 65.5669 60.7759 66.4503 61.2192 69.1069C61.2192 69.1069 76.5157 71.8302 76.3724 88.6834H76.3691Z" fill="#002140"/> +<path id="Vector_14" d="M51.4826 63.1369C51.4826 63.1369 50.4859 61.4769 48.276 61.5869C46.0626 61.6969 45.2893 63.1368 45.2893 64.0201C45.2893 64.9035 46.616 68.3335 51.3726 69.3302C56.1292 70.3268 60.8825 69.5502 60.8825 69.5502C60.8825 69.5502 60.3292 66.0102 64.4225 64.6835C68.5157 63.3569 73.3057 61.3303 73.3824 60.037C73.4557 58.747 72.499 57.1603 71.3924 57.3837C71.3924 57.3837 72.609 54.8403 70.7291 54.067C68.8491 53.2936 64.9791 54.5104 62.8758 57.827C62.8758 57.827 62.1025 50.417 57.6792 48.867C54.9159 47.6503 54.3625 51.4104 54.3625 51.4104C54.3625 51.4104 61.1092 57.9369 56.4625 62.5802C53.7259 64.6269 51.4859 63.1336 51.4859 63.1336L51.4826 63.1369Z" fill="#FFFAF0"/> +<path id="Vector_15" d="M41.3093 105.383C41.3093 105.383 40.976 106.71 39.4294 106.046C37.8794 105.383 37.9927 103.503 37.9927 103.503C37.9927 103.503 35.1894 105.053 34.8961 103.283C34.6027 101.513 34.0127 101.733 35.2294 100.52C36.446 99.3064 37.8827 96.9799 37.8827 96.9799C37.8827 96.9799 35.1194 94.9432 33.6794 95.3199C32.2394 95.6966 30.1394 97.6432 30.1394 98.3065C30.1394 98.9699 29.9195 105.606 38.4327 108.04C46.9493 110.473 48.0559 107.706 48.6093 106.38C49.1626 105.053 50.2692 103.726 48.1659 102.73C46.0659 101.733 44.296 101.18 44.296 101.18L41.3093 105.383Z" fill="#002140"/> +<path id="Vector_16" d="M36.4893 53.0404L38.2592 54.5871L45.2625 46.9939L41.4292 47.5839L36.4893 53.0404Z" fill="#002140"/> +<path id="Vector_17" d="M41.1362 55.8405L44.5995 55.9904L52.2695 48.0271L49.0995 47.6571L41.1362 55.8405Z" fill="#002140"/> +<path id="Vector_18" d="M18.5 64.5402C18.5 64.5402 12.3801 65.2769 10.2434 66.6035C10.2434 66.6035 10.3901 74.1236 16.4367 72.2802C22.4833 70.4369 19.3866 64.6137 18.5 64.537V64.5402Z" fill="#002140"/> +<path id="Vector_19" d="M12.0835 75.3801C12.0835 75.3801 7.14358 75.3802 6.18359 80.1001C6.18359 80.1001 9.06022 84.0801 11.1235 84.8934C11.1235 84.8934 15.1802 84.8201 15.6935 81.2801C16.2102 77.7402 13.7035 75.4568 12.0802 75.3801H12.0835Z" fill="#002140"/> +<path id="Vector_20" d="M41.5028 69.7037C41.5028 69.7037 33.8362 66.9003 32.5062 66.977C31.1796 67.0503 31.3996 72.6536 35.7495 73.6869C40.0995 74.7202 41.4995 69.707 41.4995 69.707L41.5028 69.7037Z" fill="#002140"/> +<path id="Vector_21" d="M60.8225 77.6669L53.8925 74.3502C53.8925 74.3502 51.6792 76.3035 51.4592 77.5201C51.2392 78.7368 51.8292 83.0501 55.0725 83.4201C58.3158 83.7901 60.9725 81.0601 60.8225 77.6702V77.6669Z" fill="#002140"/> +<path id="Vector_22" d="M67.529 82.4601C67.529 82.4601 64.8024 83.1967 64.9491 86.3666C65.0957 89.5366 69.4457 90.1999 69.4457 90.1999C69.4457 90.1999 71.659 84.2266 67.529 82.4567V82.4601Z" fill="#002140"/> +<path id="Vector_23" d="M54.5525 97.9432L59.4924 98.3865C59.4924 98.3865 62.6624 95.8798 61.9991 93.5199C61.3358 91.1599 59.4191 89.2433 56.3958 89.4633C53.3725 89.6833 52.7592 92.6899 52.6358 93.4432C52.0458 95.4332 54.0358 97.7932 54.5525 97.9399V97.9432Z" fill="#002140"/> +<path id="Vector_24" d="M34.646 101.703L38.1126 97.1333C38.1126 97.1333 33.7627 93.6667 31.1094 96.9867C28.4561 100.303 32.806 104.95 32.806 104.95C32.806 104.95 38.706 109.3 43.1293 109.447C47.5526 109.593 48.9525 106.13 48.9525 106.13L48.7325 103.55L44.4559 101.043L40.476 106.28C40.476 106.28 39.3693 106.207 38.706 105.987C38.0426 105.767 38.2626 103.333 38.2626 103.333C38.2626 103.333 35.7193 104.253 35.2227 103.757C34.726 103.26 34.6527 101.71 34.6527 101.71L34.646 101.703Z" fill="#002140"/> +<path id="Vector_25" d="M57.946 98.6066C57.946 98.6066 60.4527 104.063 62.886 104.356L66.7926 100.743L63.476 97.2032L57.946 98.6031V98.6066Z" fill="#002140"/> +<path id="Vector_26" d="M30.2966 56.5803C30.2966 56.5803 42.6832 66.9769 53.8531 57.7969" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_27" d="M42.9031 70.0703C32.9332 67.1137 11.3667 58.217 5.58345 72.2202C1.86348 81.2234 11.0534 85.6201 18.1367 87.8301C27.7799 90.84 37.4198 93.88 47.1497 96.6C54.6496 98.6933 64.3429 100.733 68.7628 92.3933C69.5195 90.9634 70.0328 89.38 70.0428 87.7633C70.0728 82.0801 63.4162 78.9168 59.1296 76.7202C53.9563 74.0702 48.483 71.7236 42.9031 70.0703Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_28" d="M26.9794 85.7766C30.7662 85.7766 33.836 82.7562 33.836 79.0301C33.836 75.3041 30.7662 72.2834 26.9794 72.2834C23.1926 72.2834 20.1228 75.3041 20.1228 79.0301C20.1228 82.7562 23.1926 85.7766 26.9794 85.7766Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_29" d="M24.2501 78.1835L28.3867 78.4434C28.51 76.0001 27.0634 73.8634 24.9501 73.5268C22.6568 73.1635 20.4535 75.0535 20.0235 77.7468C19.5935 80.4401 21.1068 82.92 23.4001 83.2834C24.7367 83.4967 26.0401 82.9402 26.9801 81.9102L24.2468 78.1835H24.2501Z" fill="#002140"/> +<path id="Vector_30" d="M40.3959 90.0533C44.1827 90.0533 47.2525 87.0328 47.2525 83.3067C47.2525 79.5807 44.1827 76.5601 40.3959 76.5601C36.6091 76.5601 33.5393 79.5807 33.5393 83.3067C33.5393 87.0328 36.6091 90.0533 40.3959 90.0533Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_31" d="M37.6696 82.4601L41.8062 82.72C41.9296 80.2767 40.4829 78.1401 38.3696 77.8034C36.0763 77.4401 33.873 79.3301 33.443 82.0234C33.0163 84.7167 34.5263 87.1966 36.8196 87.56C38.1563 87.7733 39.4596 87.2166 40.3996 86.1866L37.6663 82.4601H37.6696Z" fill="#002140"/> +<path id="Vector_32" d="M47.733 68.3403C47.733 68.3403 41.9631 68.9135 39.5198 69.0135" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_33" d="M35.383 52.3771C35.383 52.3771 34.533 11.3774 34.533 10.0274C34.533 8.67739 33.9464 1.39422 42.9063 1.28088C50.4262 1.28088 51.8662 5.37422 51.8662 8.91085C51.7562 10.3475 52.4128 52.817 52.4128 52.817" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_34" d="M35.4932 47.5104C35.4932 47.5104 46.2197 46.2938 51.973 47.6204" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_35" d="M36.3428 53.187L41.946 47.0671" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_36" d="M38.4795 54.5136L45.4894 47.0038" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_37" d="M41.5029 55.547L49.2462 47.2039" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_38" d="M44.4526 56.0638L52.1959 47.7205" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_39" d="M63.9929 60.7804C67.9729 58.5671 72.6928 56.4303 73.5028 58.937C74.3128 61.4436 68.3429 63.7304 64.1396 65.057C59.9363 66.3837 61.3363 69.4803 61.3363 69.4803C61.3363 69.4803 57.3563 70.5136 53.6897 70.0803C51.043 69.7703 47.5697 68.7136 45.8464 66.5237C44.0064 64.1837 46.6764 60.4804 49.4764 61.6671C50.143 61.9504 50.6664 62.4837 51.2564 62.9037C52.7797 63.9837 55.2263 64.3937 56.623 62.9037C59.3463 60.0038 57.7963 53.3837 54.4097 51.597" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_40" d="M71.1423 57.7571C71.1423 57.7571 72.6923 55.6204 71.069 54.2904C69.4457 52.9637 64.6557 54.1437 62.5891 58.7137C63.0524 57.6903 62.3958 55.3437 62.1624 54.3371C61.5224 51.5638 59.6658 48.6171 56.4558 48.7071C54.8192 48.7504 54.1825 50.1305 54.4058 51.6005C52.7459 52.1538 51.4192 55.1404 44.6726 56.0237C37.926 56.9071 35.936 51.7105 31.7327 50.4939C27.5295 49.2772 24.6561 52.4838 23.8628 54.9038C22.8695 57.3104 22.8962 62.4737 28.8961 65.9136" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_41" d="M30.6297 109.776L21.1165 88.7633" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_42" d="M42.6826 117.85C42.6826 117.85 43.8993 118.403 46.5526 118.293C49.2059 118.183 58.0558 116.193 58.0558 108.783C58.0558 101.373 51.5292 97.7233 51.5292 97.7233" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_43" d="M30.2962 100.596C30.2962 100.596 30.7395 104.577 36.7094 107.453C39.5161 108.803 44.686 110.643 47.4793 108.263C48.9693 106.993 49.876 104.123 47.9927 102.81C46.3893 101.693 44.5094 101.066 42.7461 100.24C41.3661 99.5932 40.0261 98.8232 38.9261 97.7532C37.2528 96.1232 34.6095 94.7233 32.2862 95.8899C30.5562 96.7599 30.1028 98.8232 30.2995 100.6L30.2962 100.596Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_44" d="M37.8728 97.2233C37.8728 97.2233 36.4895 99.2698 35.1628 100.93C33.8362 102.59 34.7762 103.42 35.2195 103.75C35.6628 104.083 36.9161 104.45 38.2595 102.55C39.6061 100.65 40.5828 99.2698 40.5828 99.2698" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_45" d="M41.3393 99.7399C40.9226 100.347 39.8493 101.883 38.8093 103.18C37.4827 104.84 38.4226 105.67 38.866 106C39.3093 106.333 40.5626 106.7 41.9059 104.8C43.2526 102.9 44.2293 101.52 44.2293 101.52" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_46" d="M36.4897 123.38C36.4897 123.38 38.3697 123.546 39.3664 122.22C40.363 120.893 39.8097 117.353 35.1631 115.03" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_47" d="M40.016 120.486C40.656 120.416 41.6193 120.17 42.2426 119.343C43.2393 118.016 42.6859 114.476 38.0393 112.153C38.0393 112.153 33.8927 110.163 31.1261 110.163C24.3795 110.163 19.9562 113.04 20.0095 116.8C20.0095 116.8 20.1762 121.28 23.6595 121.17C27.1428 121.06 28.0828 120.95 28.9128 122.22C29.7428 123.493 30.9027 126.533 33.1694 126.533C35.436 126.533 36.266 125.373 36.266 123.656C36.266 121.94 33.4461 118.9 30.9027 117.573" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_48" d="M21.4496 113.04H9.143C8.63634 113.04 8.36301 112.443 8.69634 112.06L23.5529 94.8999" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_49" d="M21.3363 89.15L2.41652 110.343C-0.766781 113.91 1.7632 119.563 6.54648 119.563H20.5063" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_50" d="M79.3291 112.35L57.5027 112.32" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_51" d="M47.3191 118.226L77.6321 118.413" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_52" d="M58.0195 110.257L84.5459 81.8135C84.5459 81.8135 84.5659 81.79 84.5759 81.7767C84.8259 81.4634 88.5592 76.8534 93.1892 76.8534C97.8191 76.8534 99.4891 81.4968 97.9825 85.3335L87.1392 112.913C87.0492 113.14 86.8392 113.296 86.5992 113.316C85.3326 113.42 81.3826 113.65 79.916 112.663C79.6793 112.506 79.596 112.2 79.6893 111.933L86.8826 91.8067C87.1159 91.15 86.2792 90.65 85.8126 91.1633L66.7961 112.103" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_53" d="M79.6888 111.93L76.3055 122.57C76.3055 122.57 75.2722 125.223 78.6655 125.223H96.802C96.802 125.223 100.709 124.853 100.709 121.83C100.709 121.83 100.929 120.356 99.4553 119.323C97.982 118.29 87.1387 112.91 87.1387 112.91" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_54" d="M86.7759 117.406C86.7759 117.406 87.7359 114.68 89.3558 114.236" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_55" d="M91.1255 119.546C91.1255 119.546 92.0855 116.82 93.7055 116.376" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_56" d="M99.9724 119.473C99.9724 119.473 129.465 119.503 132.025 119.503C134.585 119.503 134.679 115.563 134.679 115.563V99.0066C134.679 99.0066 134.309 95.0999 131.285 95.0999C131.285 95.0999 129.812 94.8798 128.779 96.3531C127.745 97.8265 121.206 111.953 121.206 111.953C121.206 111.953 121.942 117.703 121.132 119.4" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_57" d="M126.865 109.033C126.865 109.033 124.139 108.073 123.695 106.453" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_58" d="M129.002 104.683C129.002 104.683 126.275 103.723 125.832 102.103" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_59" d="M87.6592 112.026H121.209" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_60" d="M57.946 98.6798C57.946 98.6798 59.496 103.25 63.3293 104.283" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_61" d="M63.6223 97.5C63.6223 97.5 64.729 99.8598 66.8656 100.596" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_62" d="M61.3359 69.4835C61.3359 69.4835 76.6291 70.8602 76.6291 90.3" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_63" d="M63.4023 65.5004C63.4023 65.5004 82.2921 70.2236 82.2921 84.2302" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_64" d="M54.3392 97.91C53.2292 97.0534 52.5159 95.7099 52.5159 94.1999C52.5159 91.6133 54.6125 89.5167 57.1992 89.5167C59.7858 89.5167 61.8824 91.6133 61.8824 94.1999C61.8824 95.8966 60.9824 97.38 59.6325 98.2033" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_65" d="M69.5392 90.2333C69.3525 90.2599 69.1625 90.2733 68.9692 90.2733C66.7492 90.2733 64.9492 88.4733 64.9492 86.2533C64.9492 84.4567 66.1292 82.9334 67.7592 82.42" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_66" d="M60.6391 77.7201C60.7091 78.0468 60.7491 78.3869 60.7491 78.7369C60.7491 81.3635 58.6191 83.4935 55.9925 83.4935C53.3658 83.4935 51.2358 81.3635 51.2358 78.7369C51.2358 76.8469 52.3392 75.2136 53.9358 74.4469" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_67" d="M6.34354 80.8467C6.31354 80.6267 6.29688 80.4001 6.29688 80.1701C6.29688 77.5434 8.42685 75.4135 11.0535 75.4135C13.6801 75.4135 15.8101 77.5434 15.8101 80.1701C15.8101 82.6534 13.9101 84.69 11.4835 84.9067" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_68" d="M18.9128 64.6603C19.5162 65.4703 19.8762 66.4769 19.8762 67.5636C19.8762 70.2469 17.6995 72.4235 15.0162 72.4235C12.3329 72.4235 10.1562 70.2469 10.1562 67.5636C10.1562 67.2769 10.1829 66.9935 10.2296 66.7202" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +<path id="Vector_69" d="M41.6829 69.8403C41.2429 72.0703 39.2729 73.7535 36.9129 73.7535C34.2296 73.7535 32.053 71.5768 32.053 68.8935C32.053 68.2402 32.183 67.617 32.4163 67.0503" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> +</g> +</svg> From 93c5a871780ceda39b4c52b816eca8b261840bbe Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 19:14:03 -0800 Subject: [PATCH 20/81] Implement confirm page --- src/components/Icon/Illustrations.ts | 2 ++ src/languages/en.ts | 3 ++- src/languages/es.ts | 3 ++- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 24 +++++++++++++------ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 954c8d0392fc..02936225d0c7 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -16,6 +16,7 @@ import Lounge from '@assets/images/product-illustrations/lounge.svg'; import MagicCode from '@assets/images/product-illustrations/magic-code.svg'; import MoneyEnvelopeBlue from '@assets/images/product-illustrations/money-envelope--blue.svg'; import MoneyMousePink from '@assets/images/product-illustrations/money-mouse--pink.svg'; +import MushroomTopHat from '@assets/images/product-illustrations/mushroom-top-hat.svg'; import PaymentHands from '@assets/images/product-illustrations/payment-hands.svg'; import ReceiptYellow from '@assets/images/product-illustrations/receipt--yellow.svg'; import ReceiptsSearchYellow from '@assets/images/product-illustrations/receipts-search--yellow.svg'; @@ -82,6 +83,7 @@ export { Mailbox, MoneyEnvelopeBlue, MoneyMousePink, + MushroomTopHat, ReceiptsSearchYellow, ReceiptYellow, RocketBlue, diff --git a/src/languages/en.ts b/src/languages/en.ts index f0fbd3a9272c..faf5e6568f81 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2146,7 +2146,8 @@ export default { [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Why to you prefer Expensify Classic?', }, responsePlaceholder: 'Your response', - thankYou: 'Your responses will help us build a better product to get stuff done. Thank you so much!', + thankYou: 'Thanks for the feedback!', + thankYouSubtitle: 'Your responses will help us build a better product to get stuff done. Thank you so much!', goToExpensifyClassic: 'Switch to Expensify Classic', offline: "You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.", diff --git a/src/languages/es.ts b/src/languages/es.ts index 5666598880a3..f96f22976d23 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2634,7 +2634,8 @@ export default { [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', }, responsePlaceholder: 'Su respuesta', - thankYou: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', + thankYou: '¡Gracias por sus comentarios!', + thankYouSubtitle: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', goToExpensifyClassic: 'Cambiar a Expensify Classic', offline: 'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.', diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index a7d2ebad8214..284cc88fd015 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,15 +1,20 @@ import React from 'react'; +import {View} from 'react-native'; import Button from '@components/Button'; +import FixedFooter from '@components/FixedFooter'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {MushroomTopHat} from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; function ExitSurveyConfirmPage() { const {translate} = useLocalize(); + const styles = useThemeStyles(); return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> @@ -17,13 +22,18 @@ function ExitSurveyConfirmPage() { title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> - <Text>Confirm page</Text> - <Button - text="Next" - onPress={() => { - Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); - }} - /> + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> + <MushroomTopHat /> + <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> + <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> + </View> + <FixedFooter> + <Button + success + text={translate('exitSurvey.goToExpensifyClassic')} + onPress={() => Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX)} + /> + </FixedFooter> </ScreenWrapper> ); } From 3035ce05bef77f5f4ad6a966035940f43488dccc Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 20:08:03 -0800 Subject: [PATCH 21/81] Add Onyx types for forms --- src/CONST.ts | 12 ++++++++---- src/ONYXKEYS.ts | 6 ++++++ src/ROUTES.ts | 2 +- src/languages/en.ts | 12 ++++++------ src/languages/es.ts | 12 ++++++------ src/libs/Navigation/types.ts | 2 +- .../settings/ExitSurvey/ExitSurveyReasonPage.tsx | 12 +++++------- .../settings/ExitSurvey/ExitSurveyResponsePage.tsx | 6 ++---- 8 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 13599172a902..8b757e51ceae 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3165,10 +3165,14 @@ const CONST = { REPORT_FIELD_TITLE_FIELD_ID: 'text_title', - EXIT_SURVEY_REASONS: { - FEATURE_NOT_AVAILABLE: 'featureNotAvailable', - DONT_UNDERSTAND: 'dontUnderstand', - PREFER_CLASSIC: 'preferClassic', + EXIT_SURVEY: { + REASONS: { + FEATURE_NOT_AVAILABLE: 'featureNotAvailable', + DONT_UNDERSTAND: 'dontUnderstand', + PREFER_CLASSIC: 'preferClassic', + }, + REASON_INPUT_ID: 'reason', + RESPONSE_INPUT_ID: 'response', }, } as const; diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index c16a9972820e..82bfb3847896 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -542,6 +542,12 @@ type OnyxValues = { [ONYXKEYS.FORMS.GET_PHYSICAL_CARD_FORM_DRAFT]: OnyxTypes.Form | undefined; [ONYXKEYS.FORMS.POLICY_REPORT_FIELD_EDIT_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.POLICY_REPORT_FIELD_EDIT_FORM_DRAFT]: OnyxTypes.Form | undefined; + [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.Form & { + [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + }; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.Form & { + [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: string; + }; }; type OnyxKeyValue<TOnyxKey extends (OnyxKey | OnyxCollectionKey) & keyof OnyxValues> = OnyxEntry<OnyxValues[TOnyxKey]>; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index f9c79e729161..0a9e81d7326b 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -152,7 +152,7 @@ const ROUTES = { SETTINGS_EXIT_SURVEY_REASON: 'settings/exit-survey/reason', SETTINGS_EXIT_SURVEY_RESPONSE: { route: 'settings/exit-survey/response', - getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => `settings/exit-survey/response?reason=${encodeURIComponent(reason)}` as const, + getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) => `settings/exit-survey/response?reason=${encodeURIComponent(reason)}` as const, }, SETTINGS_EXIT_SURVEY_CONFIRM: 'settings/exit-survey/confirm', diff --git a/src/languages/en.ts b/src/languages/en.ts index faf5e6568f81..7a8c6272addc 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2136,14 +2136,14 @@ export default { }, reasons: { // TODO: use consts for these keys - [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "I need a feature that's only available in Expensify Classic.", - [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: "I don't understand how to use New Expensify.", - [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'I understand how to use New Expensify, but I prefer Expensify Classic.', + [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: "I need a feature that's only available in Expensify Classic.", + [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: "I don't understand how to use New Expensify.", + [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'I understand how to use New Expensify, but I prefer Expensify Classic.', }, prompts: { - [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: "What feature do you need that isn't available in New Expensify?", - [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: 'What are you trying to do?', - [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Why to you prefer Expensify Classic?', + [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: "What feature do you need that isn't available in New Expensify?", + [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: 'What are you trying to do?', + [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'Why to you prefer Expensify Classic?', }, responsePlaceholder: 'Your response', thankYou: 'Thanks for the feedback!', diff --git a/src/languages/es.ts b/src/languages/es.ts index f96f22976d23..0dedfe8c4af3 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2624,14 +2624,14 @@ export default { }, reasons: { // TODO: use consts for these keys - [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: 'Necesito una función que sólo está disponible en Expensify Classic.', - [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: 'No entiendo cómo usar New Expensify.', - [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', + [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: 'Necesito una función que sólo está disponible en Expensify Classic.', + [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: 'No entiendo cómo usar New Expensify.', + [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', }, prompts: { - [CONST.EXIT_SURVEY_REASONS.FEATURE_NOT_AVAILABLE]: '¿Qué función necesitas que no esté disponible en New Expensify?', - [CONST.EXIT_SURVEY_REASONS.DONT_UNDERSTAND]: '¿Qué estás tratando de hacer?', - [CONST.EXIT_SURVEY_REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', + [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: '¿Qué función necesitas que no esté disponible en New Expensify?', + [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: '¿Qué estás tratando de hacer?', + [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', }, responsePlaceholder: 'Su respuesta', thankYou: '¡Gracias por sus comentarios!', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 24504fccb3f8..5d02ad1f44c9 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -126,7 +126,7 @@ type SettingsNavigatorParamList = { [SCREENS.KEYBOARD_SHORTCUTS]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { - reason: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>; + reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; }; [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: undefined; }; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 7de412a3a581..dcf7fb839290 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -14,16 +14,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -const REASON_INPUT_ID = 'reason'; - function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [reason, setReason] = useState<ValueOf<typeof CONST.EXIT_SURVEY_REASONS>>(); + const [reason, setReason] = useState<ValueOf<typeof CONST.EXIT_SURVEY.REASONS>>(); const reasons: Choice[] = useMemo( () => - Object.values(CONST.EXIT_SURVEY_REASONS).map((value) => ({ + Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ value, label: translate(`exitSurvey.reasons.${value}`), style: styles.mt6, @@ -46,7 +44,7 @@ function ExitSurveyReasonPage() { return {}; } return { - [REASON_INPUT_ID]: translate('common.error.fieldRequired'), + [CONST.EXIT_SURVEY.REASON_INPUT_ID]: translate('common.error.fieldRequired'), }; }} onSubmit={() => { @@ -65,9 +63,9 @@ function ExitSurveyReasonPage() { <InputWrapper // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={RadioButtons} - inputID={REASON_INPUT_ID} + inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} items={reasons} - onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY_REASONS>) => setReason(value)} + onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) => setReason(value)} /> </> </FormProvider> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 2e9d5cc4ab26..cc1b6ed237c7 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -21,8 +21,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -const RESPONSE_INPUT_ID = 'response'; - type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { @@ -82,7 +80,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { return {}; } return { - [RESPONSE_INPUT_ID]: translate('common.error.fieldRequired'), + [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: translate('common.error.fieldRequired'), }; }} shouldValidateOnBlur @@ -93,7 +91,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { <InputWrapper // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={TextInput} - inputID={RESPONSE_INPUT_ID} + inputID={CONST.EXIT_SURVEY.RESPONSE_INPUT_ID} label={translate(`exitSurvey.responsePlaceholder`)} accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} role={CONST.ROLE.PRESENTATION} From b83bdef31f75d7a6bcf64813e7921f09dc6a493a Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 20:09:58 -0800 Subject: [PATCH 22/81] Fix route type --- src/libs/Navigation/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 5d02ad1f44c9..dfea45e1a796 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -126,7 +126,7 @@ type SettingsNavigatorParamList = { [SCREENS.KEYBOARD_SHORTCUTS]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { - reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; }; [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: undefined; }; From 733dd6ac4e232a27724afcf4608ad0dc8def619a Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 24 Jan 2024 20:17:47 -0800 Subject: [PATCH 23/81] Add obligatory draft keys for forms --- src/ONYXKEYS.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 82bfb3847896..02d3b77b8986 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -359,7 +359,9 @@ const ONYXKEYS = { POLICY_REPORT_FIELD_EDIT_FORM: 'policyReportFieldEditForm', POLICY_REPORT_FIELD_EDIT_FORM_DRAFT: 'policyReportFieldEditFormDraft', EXIT_SURVEY_REASON_FORM: 'exitSurveyReasonForm', + EXIT_SURVEY_REASON_FORM_DRAFT: 'exitSurveyReasonFormDraft', EXIT_SURVEY_RESPONSE_FORM: 'exitSurveyResponseForm', + EXIT_SURVEY_RESPONSE_FORM_DRAFT: 'exitSurveyResponseFormDraft', }, } as const; @@ -545,9 +547,11 @@ type OnyxValues = { [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.Form & { [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; }; + [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.Form & { [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: string; }; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.Form; }; type OnyxKeyValue<TOnyxKey extends (OnyxKey | OnyxCollectionKey) & keyof OnyxValues> = OnyxEntry<OnyxValues[TOnyxKey]>; From ca73fcf60201b19cdbd19fbfbe6c86eb3ba61e18 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 26 Jan 2024 09:35:51 -0800 Subject: [PATCH 24/81] Make spanish copy informal --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 0dedfe8c4af3..a7a51de6ff67 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2634,7 +2634,7 @@ export default { [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: '¿Por qué prefieres Expensify Classic?', }, responsePlaceholder: 'Su respuesta', - thankYou: '¡Gracias por sus comentarios!', + thankYou: '¡Gracias por tus comentarios!', thankYouSubtitle: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', goToExpensifyClassic: 'Cambiar a Expensify Classic', offline: From 99b780944dca5639f33e5c9ee0039b5b5a45edb4 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 26 Jan 2024 17:10:57 -0800 Subject: [PATCH 25/81] Hook up with API --- src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 6 +++++- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 2 ++ src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 6 +++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 284cc88fd015..e03515ab3742 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -9,6 +9,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import * as ExitSurvey from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; @@ -31,7 +32,10 @@ function ExitSurveyConfirmPage() { <Button success text={translate('exitSurvey.goToExpensifyClassic')} - onPress={() => Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX)} + onPress={() => { + ExitSurvey.switchToOldDot(); + Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + }} /> </FixedFooter> </ScreenWrapper> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index dcf7fb839290..f1fea45dd0ca 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -10,6 +10,7 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import * as ExitSurvey from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -51,6 +52,7 @@ function ExitSurveyReasonPage() { if (!reason) { return; } + ExitSurvey.saveExitReason(reason); Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason)); }} submitButtonText={translate('common.next')} diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index cc1b6ed237c7..8015522ed07e 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -16,6 +16,7 @@ import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; import type {SettingsNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; +import * as ExitSurvey from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -73,7 +74,10 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} - onSubmit={() => Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM)} + onSubmit={() => { + ExitSurvey.saveResponse(response); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM); + }} submitButtonText={translate('common.next')} validate={() => { if (response?.trim()) { From fa47d7f6eaeabea2d8228016a8af689a48bfe563 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 26 Jan 2024 17:12:27 -0800 Subject: [PATCH 26/81] whoops, forgot to commit this file --- src/libs/actions/ExitSurvey.ts | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/libs/actions/ExitSurvey.ts diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts new file mode 100644 index 000000000000..532e850a4cab --- /dev/null +++ b/src/libs/actions/ExitSurvey.ts @@ -0,0 +1,45 @@ +import Onyx from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import * as API from '@libs/API'; +import Log from '@libs/Log'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +let exitReason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS> | undefined; +let exitSurveyResponse: string | undefined; +Onyx.connect({ + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, + callback: (value) => (exitReason = value?.[CONST.EXIT_SURVEY.REASON_INPUT_ID]), +}); +Onyx.connect({ + key: ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, + callback: (value) => (exitSurveyResponse = value?.[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]), +}); + +function saveExitReason(reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) { + console.log('RORY_DEBUG setting exit reason:', reason); + Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); +} + +function saveResponse(response: string) { + console.log('RORY_DEBUG setting response:', response); + Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: response}); +} + +/** + * Save the user's response to the mandatory exit survey in the back-end. + */ +function switchToOldDot() { + if (!exitReason || !exitSurveyResponse) { + console.log('RORY_DEBUG', {exitReason, exitSurveyResponse}); + Log.hmmm('Attempted to call SwitchToOldDot without filling out mandatory survey.'); + return; + } + + API.write('SwitchToOldDot', { + reason: exitReason, + surveyResponse: exitSurveyResponse, + }); +} + +export {saveExitReason, saveResponse, switchToOldDot}; From fb01ab5ccc533070f098076440cfe341176aa635 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 26 Jan 2024 17:18:31 -0800 Subject: [PATCH 27/81] Fix types for API params --- src/libs/API/parameters/SwitchToOldDotParams.ts | 9 +++++++++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 ++ src/libs/actions/ExitSurvey.ts | 3 --- 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 src/libs/API/parameters/SwitchToOldDotParams.ts diff --git a/src/libs/API/parameters/SwitchToOldDotParams.ts b/src/libs/API/parameters/SwitchToOldDotParams.ts new file mode 100644 index 000000000000..7fe363a542e4 --- /dev/null +++ b/src/libs/API/parameters/SwitchToOldDotParams.ts @@ -0,0 +1,9 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type SwitchToOldDotParams = { + reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + surveyResponse: string; +}; + +export default SwitchToOldDotParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 039398c0fbf6..836802cd5a1c 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -122,3 +122,4 @@ export type {default as ReopenTaskParams} from './ReopenTaskParams'; export type {default as CompleteTaskParams} from './CompleteTaskParams'; export type {default as CompleteEngagementModalParams} from './CompleteEngagementModalParams'; export type {default as SetNameValuePairParams} from './SetNameValuePairParams'; +export type {default as SwitchToOldDotParams} from './SwitchToOldDotParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index f58ebc30b4a2..073e2c0fc694 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -113,6 +113,7 @@ const WRITE_COMMANDS = { COMPLETE_TASK: 'CompleteTask', COMPLETE_ENGAGEMENT_MODAL: 'CompleteEngagementModal', SET_NAME_VALUE_PAIR: 'SetNameValuePair', + SWITCH_TO_OLD_DOT: 'SwitchToOldDot', } as const; type WriteCommand = ValueOf<typeof WRITE_COMMANDS>; @@ -223,6 +224,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.COMPLETE_TASK]: Parameters.CompleteTaskParams; [WRITE_COMMANDS.COMPLETE_ENGAGEMENT_MODAL]: Parameters.CompleteEngagementModalParams; [WRITE_COMMANDS.SET_NAME_VALUE_PAIR]: Parameters.SetNameValuePairParams; + [WRITE_COMMANDS.SWITCH_TO_OLD_DOT]: Parameters.SwitchToOldDotParams; }; const READ_COMMANDS = { diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index 532e850a4cab..766d0463257a 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -17,12 +17,10 @@ Onyx.connect({ }); function saveExitReason(reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) { - console.log('RORY_DEBUG setting exit reason:', reason); Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); } function saveResponse(response: string) { - console.log('RORY_DEBUG setting response:', response); Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: response}); } @@ -31,7 +29,6 @@ function saveResponse(response: string) { */ function switchToOldDot() { if (!exitReason || !exitSurveyResponse) { - console.log('RORY_DEBUG', {exitReason, exitSurveyResponse}); Log.hmmm('Attempted to call SwitchToOldDot without filling out mandatory survey.'); return; } From 6f7026f9842f866c463ff83bd9a822c8e0ec77a0 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 26 Jan 2024 17:34:17 -0800 Subject: [PATCH 28/81] Fix regression: https://github.com/Expensify/App/pull/34787\#issuecomment-1912900085 --- src/components/TextInput/BaseTextInput/index.native.tsx | 2 +- src/components/TextInput/BaseTextInput/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TextInput/BaseTextInput/index.native.tsx b/src/components/TextInput/BaseTextInput/index.native.tsx index afd796d8c878..7a230bc90fe4 100644 --- a/src/components/TextInput/BaseTextInput/index.native.tsx +++ b/src/components/TextInput/BaseTextInput/index.native.tsx @@ -426,7 +426,7 @@ function BaseTextInput( styles.visibilityHidden, ]} onLayout={(e) => { - if (e.nativeEvent.layout.width === 0) { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { return; } setTextInputWidth(e.nativeEvent.layout.width); diff --git a/src/components/TextInput/BaseTextInput/index.tsx b/src/components/TextInput/BaseTextInput/index.tsx index 4fa47eff4128..7ca8c69153f1 100644 --- a/src/components/TextInput/BaseTextInput/index.tsx +++ b/src/components/TextInput/BaseTextInput/index.tsx @@ -447,7 +447,7 @@ function BaseTextInput( styles.visibilityHidden, ]} onLayout={(e) => { - if (e.nativeEvent.layout.width === 0) { + if (e.nativeEvent.layout.width === 0 && e.nativeEvent.layout.height === 0) { return; } let additionalWidth = 0; From bf336ce123bfdeae30bcfa35e053d7248b79761b Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Mon, 29 Jan 2024 17:38:24 -0800 Subject: [PATCH 29/81] Bump type-fest --- package-lock.json | 57 ++++++++++++++++++++++++++++++++--------------- package.json | 2 +- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1328d498c79..db7ccddb2e84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -230,7 +230,7 @@ "shellcheck": "^1.1.0", "style-loader": "^2.0.0", "time-analytics-webpack-plugin": "^0.1.17", - "type-fest": "^3.12.0", + "type-fest": "^4.10.1", "typescript": "^5.3.2", "wait-port": "^0.2.9", "webpack": "^5.76.0", @@ -7926,9 +7926,9 @@ } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", + "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", "dev": true, "dependencies": { "ansi-html-community": "^0.0.8", @@ -7948,7 +7948,7 @@ "@types/webpack": "4.x || 5.x", "react-refresh": ">=0.10.0 <1.0.0", "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", + "type-fest": ">=0.17.0 <5.0.0", "webpack": ">=4.43.0 <6.0.0", "webpack-dev-server": "3.x || 4.x", "webpack-hot-middleware": "2.x", @@ -26744,10 +26744,11 @@ } }, "node_modules/core-js-pure": { - "version": "3.24.1", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", + "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -38453,6 +38454,17 @@ "node": ">=8" } }, + "node_modules/jest-watch-typeahead/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-watcher": { "version": "29.4.1", "license": "MIT", @@ -50805,11 +50817,12 @@ } }, "node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", + "integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", + "dev": true, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -59194,9 +59207,9 @@ "optional": true }, "@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", + "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", "dev": true, "requires": { "ansi-html-community": "^0.0.8", @@ -72791,7 +72804,9 @@ } }, "core-js-pure": { - "version": "3.24.1", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", + "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", "dev": true }, "core-util-is": { @@ -81177,6 +81192,11 @@ "requires": { "has-flag": "^4.0.0" } + }, + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" } } }, @@ -90002,9 +90022,10 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", + "integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", + "dev": true }, "type-is": { "version": "1.6.18", diff --git a/package.json b/package.json index 96de7fb0ab77..fda255265071 100644 --- a/package.json +++ b/package.json @@ -278,7 +278,7 @@ "shellcheck": "^1.1.0", "style-loader": "^2.0.0", "time-analytics-webpack-plugin": "^0.1.17", - "type-fest": "^3.12.0", + "type-fest": "^4.10.1", "typescript": "^5.3.2", "wait-port": "^0.2.9", "webpack": "^5.76.0", From d85a2fe95b55d3307f4de4ab375fd128b750981c Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 30 Jan 2024 10:42:08 -0800 Subject: [PATCH 30/81] Wrap RadioButtons with forwardRef --- src/components/RadioButtons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 0055f23ca29d..047fb72f420d 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {forwardRef, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -51,4 +51,4 @@ function RadioButtons({items, onPress, errorText}: RadioButtonsProps) { RadioButtons.displayName = 'RadioButtons'; export type {Choice}; -export default RadioButtons; +export default forwardRef(RadioButtons); From e1e5283c1512a060e3ea26591c482e3f35950ded Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 30 Jan 2024 10:43:28 -0800 Subject: [PATCH 31/81] Add ExitSurveyForm onyx types --- src/ONYXKEYS.ts | 11 +++-------- src/types/onyx/index.ts | 14 +++++++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index b90916804160..9b6287f62def 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -545,15 +545,10 @@ type OnyxValues = { // @ts-expect-error Different values are defined under the same key: ReimbursementAccount and ReimbursementAccountForm [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM]: OnyxTypes.Form; [ONYXKEYS.FORMS.REIMBURSEMENT_ACCOUNT_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.POLICY_REPORT_FIELD_EDIT_FORM_DRAFT]: OnyxTypes.Form | undefined; - [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.Form & { - [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; - }; + [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.ExitSurveyReasonForm; [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT]: OnyxTypes.Form; - [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.Form & { - [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: string; - }; - [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.Form; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.ExitSurveyResponseForm; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.ExitSurveyResponseForm; }; type OnyxKeyValue<TOnyxKey extends (OnyxKey | OnyxCollectionKey) & keyof OnyxValues> = OnyxEntry<OnyxValues[TOnyxKey]>; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 5b04cae58671..8f7afa24f03d 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -9,7 +9,17 @@ import type Credentials from './Credentials'; import type Currency from './Currency'; import type CustomStatusDraft from './CustomStatusDraft'; import type Download from './Download'; -import type {AddDebitCardForm, DateOfBirthForm, DisplayNameForm, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, PrivateNotesForm} from './Form'; +import type { + AddDebitCardForm, + DateOfBirthForm, + DisplayNameForm, + ExitSurveyReasonForm, + ExitSurveyResponseForm, + IKnowATeacherForm, + IntroSchoolPrincipalForm, + NewRoomForm, + PrivateNotesForm, +} from './Form'; import type Form from './Form'; import type FrequentlyUsedEmoji from './FrequentlyUsedEmoji'; import type {FundList} from './Fund'; @@ -84,6 +94,8 @@ export type { CustomStatusDraft, DateOfBirthForm, Download, + ExitSurveyReasonForm, + ExitSurveyResponseForm, Form, FrequentlyUsedEmoji, Fund, From b5cfed1c337f68b8a0422f44955f73a557d2f07e Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 30 Jan 2024 10:44:28 -0800 Subject: [PATCH 32/81] Add Onyx type in Form.ts as well --- src/types/onyx/Form.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index c3bcec2a2d3b..5759a320bfff 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -1,3 +1,5 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; import type * as OnyxCommon from './OnyxCommon'; type FormValueType = string | boolean | Date; @@ -54,6 +56,26 @@ type PrivateNotesForm = Form<{ privateNotes: string; }>; +type ExitSurveyReasonForm = Form<{ + [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; +}>; + +type ExitSurveyResponseForm = Form<{ + [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: string; +}>; + export default Form; -export type {AddDebitCardForm, DateOfBirthForm, PrivateNotesForm, DisplayNameForm, FormValueType, NewRoomForm, BaseForm, IKnowATeacherForm, IntroSchoolPrincipalForm}; +export type { + AddDebitCardForm, + DateOfBirthForm, + PrivateNotesForm, + DisplayNameForm, + FormValueType, + NewRoomForm, + BaseForm, + IKnowATeacherForm, + IntroSchoolPrincipalForm, + ExitSurveyReasonForm, + ExitSurveyResponseForm, +}; From 86bf87fed55a9b24fa9625bf706e6261c8d83cf9 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 09:55:05 -0800 Subject: [PATCH 33/81] Fix typecheck --- src/components/Form/FormProvider.tsx | 4 ++-- src/components/Form/types.ts | 15 ++++++++------- src/pages/settings/AboutPage.tsx | 8 +------- .../ExitSurvey/ExitSurveyReasonPage.tsx | 19 ++++++++++--------- .../ExitSurvey/ExitSurveyResponsePage.tsx | 17 ++++++++--------- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index 424fd989291a..ba0807c9632e 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -1,5 +1,5 @@ import lodashIsEqual from 'lodash/isEqual'; -import type {ForwardedRef, MutableRefObject, ReactNode} from 'react'; +import type {FocusEvent, ForwardedRef, MutableRefObject, ReactNode} from 'react'; import React, {createRef, forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; @@ -273,7 +273,7 @@ function FormProvider( } inputProps.onPressOut?.(event); }, - onBlur: (event) => { + onBlur: (event: FocusEvent) => { // Only run validation when user proactively blurs the input. if (Visibility.isVisible() && Visibility.hasFocus()) { const relatedTarget = event && 'relatedTarget' in event.nativeEvent && event?.nativeEvent?.relatedTarget; diff --git a/src/components/Form/types.ts b/src/components/Form/types.ts index 447f3205ad68..6a0df04c575f 100644 --- a/src/components/Form/types.ts +++ b/src/components/Form/types.ts @@ -1,9 +1,10 @@ -import type {ComponentProps, FocusEvent, Key, MutableRefObject, ReactNode, Ref} from 'react'; -import type {GestureResponderEvent, NativeSyntheticEvent, StyleProp, TextInputFocusEventData, ViewStyle} from 'react-native'; +import type {ComponentProps, Key, MutableRefObject, ReactNode, Ref} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; import type AddressSearch from '@components/AddressSearch'; import type AmountTextInput from '@components/AmountTextInput'; import type CheckboxWithLabel from '@components/CheckboxWithLabel'; import type Picker from '@components/Picker'; +import type RadioButtons from '@components/RadioButtons'; import type SingleChoiceQuestion from '@components/SingleChoiceQuestion'; import type TextInput from '@components/TextInput'; import type {OnyxFormKey, OnyxValues} from '@src/ONYXKEYS'; @@ -17,7 +18,7 @@ import type {BaseForm, FormValueType} from '@src/types/onyx/Form'; * TODO: Add remaining inputs here once these components are migrated to Typescript: * CountrySelector | StatePicker | DatePicker | EmojiPickerButtonDropdown | RoomNameInput | ValuePicker */ -type ValidInputs = typeof TextInput | typeof AmountTextInput | typeof SingleChoiceQuestion | typeof CheckboxWithLabel | typeof Picker | typeof AddressSearch; +type ValidInputs = typeof TextInput | typeof AmountTextInput | typeof SingleChoiceQuestion | typeof CheckboxWithLabel | typeof Picker | typeof AddressSearch | typeof RadioButtons; type ValueTypeKey = 'string' | 'boolean' | 'date'; @@ -26,13 +27,13 @@ type MeasureLayoutOnSuccessCallback = (left: number, top: number, width: number, type BaseInputProps = { shouldSetTouchedOnBlurOnly?: boolean; onValueChange?: (value: unknown, key: string) => void; - onTouched?: (event: GestureResponderEvent) => void; + onTouched?: (event: unknown) => void; valueType?: ValueTypeKey; value?: FormValueType; defaultValue?: FormValueType; - onBlur?: (event: FocusEvent | NativeSyntheticEvent<TextInputFocusEventData>) => void; - onPressOut?: (event: GestureResponderEvent) => void; - onPress?: (event: GestureResponderEvent) => void; + onBlur?: (event: unknown) => void; + onPressOut?: (event: unknown) => void; + onPress?: (event: unknown) => void; shouldSaveDraft?: boolean; shouldUseDefaultValue?: boolean; key?: Key | null | undefined; diff --git a/src/pages/settings/AboutPage.tsx b/src/pages/settings/AboutPage.tsx index 5bbbe3688ca8..ddc779ea23aa 100644 --- a/src/pages/settings/AboutPage.tsx +++ b/src/pages/settings/AboutPage.tsx @@ -22,14 +22,8 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; -<<<<<<<< HEAD:src/pages/settings/AboutPage.js -import pkg from '../../../package.json'; -|||||||| b4ccb0c4d1:src/pages/settings/AboutPage/AboutPage.js -import pkg from '../../../../package.json'; -======== import type IconAsset from '@src/types/utils/IconAsset'; -import pkg from '../../../../package.json'; ->>>>>>>> main:src/pages/settings/AboutPage.tsx +import pkg from '../../../package.json'; function getFlavor(): string { const bundleId = DeviceInfo.getBundleId(); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index f1fea45dd0ca..f4fb87134be1 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -14,12 +14,15 @@ import * as ExitSurvey from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; + +type Reason = ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [reason, setReason] = useState<ValueOf<typeof CONST.EXIT_SURVEY.REASONS>>(); + const [reason, setReason] = useState<Reason>(); const reasons: Choice[] = useMemo( () => Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ @@ -36,17 +39,15 @@ function ExitSurveyReasonPage() { title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> - {/* @ts-expect-error - FormProvider is not yet migrated to TS */} <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM} style={[styles.flex1, styles.mt3, styles.mh5]} validate={() => { - if (reason) { - return {}; + const errors: Errors = {}; + if (!reason) { + errors[CONST.EXIT_SURVEY.REASON_INPUT_ID] = 'common.error.fieldRequired'; } - return { - [CONST.EXIT_SURVEY.REASON_INPUT_ID]: translate('common.error.fieldRequired'), - }; + return errors; }} onSubmit={() => { if (!reason) { @@ -63,12 +64,12 @@ function ExitSurveyReasonPage() { <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> <InputWrapper - // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={RadioButtons} inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} items={reasons} - onPress={(value: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) => setReason(value)} + onPress={(value) => setReason(value as Reason)} /> + x </> </FormProvider> </ScreenWrapper> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 8015522ed07e..2b4e6b4d51e0 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -3,6 +3,7 @@ import React, {useRef, useState} from 'react'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; @@ -21,6 +22,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import type {Errors} from '@src/types/onyx/OnyxCommon'; type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; @@ -34,7 +36,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { const {reason} = route.params; const [response, setResponse] = useState(''); - const responseInputRef = useRef(null); + const responseInputRef = useRef<AnimatedTextInputRef | null>(null); const formTopMarginsStyle = styles.mt3; const textStyle = styles.headerAnonymousFooter; @@ -70,7 +72,6 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> - {/* @ts-expect-error - FormProvider is not yet migrated to TS */} <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} @@ -80,12 +81,11 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { }} submitButtonText={translate('common.next')} validate={() => { - if (response?.trim()) { - return {}; + const errors: Errors = {}; + if (!response?.trim()) { + errors[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID] = 'common.error.fieldRequired'; } - return { - [CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: translate('common.error.fieldRequired'), - }; + return errors; }} shouldValidateOnBlur shouldValidateOnChange @@ -93,7 +93,6 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { <> <Text style={textStyle}>{translate(`exitSurvey.prompts.${reason}`)}</Text> <InputWrapper - // @ts-expect-error – InputWrapper is not yet implemented in TS InputComponent={TextInput} inputID={CONST.EXIT_SURVEY.RESPONSE_INPUT_ID} label={translate(`exitSurvey.responsePlaceholder`)} @@ -101,7 +100,7 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { role={CONST.ROLE.PRESENTATION} autoGrowHeight maxLength={CONST.MAX_COMMENT_LENGTH} - ref={(el) => { + ref={(el: AnimatedTextInputRef) => { if (!el) { return; } From 688a49bba57a9ef1439fc0a2d367e7f7e6caeef6 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 10:03:24 -0800 Subject: [PATCH 34/81] Remove TODOs --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 294508db413f..e12923ff2d98 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2143,7 +2143,6 @@ export default { subtitle: 'Before you go, please tell us why you’d like to switch to Expensify Classic.', }, reasons: { - // TODO: use consts for these keys [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: "I need a feature that's only available in Expensify Classic.", [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: "I don't understand how to use New Expensify.", [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'I understand how to use New Expensify, but I prefer Expensify Classic.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 557297cd3354..0fcac0401c3a 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2631,7 +2631,6 @@ export default { subtitle: 'Antes de irte, por favor dinos por qué te gustaría cambiarte a Expensify Classic.', }, reasons: { - // TODO: use consts for these keys [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: 'Necesito una función que sólo está disponible en Expensify Classic.', [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: 'No entiendo cómo usar New Expensify.', [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'Entiendo cómo usar New Expensify, pero prefiero Expensify Classic.', From 2b2a1481cb824d770111bf9801818d997ee71268 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 10:12:55 -0800 Subject: [PATCH 35/81] Fix forwardRef --- src/components/RadioButtons.tsx | 9 +++++++-- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 047fb72f420d..12447b1cb97d 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -1,4 +1,5 @@ import React, {forwardRef, useState} from 'react'; +import type {ForwardedRef} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -23,13 +24,17 @@ type RadioButtonsProps = { errorText?: MaybePhraseKey; }; -function RadioButtons({items, onPress, errorText}: RadioButtonsProps) { +function RadioButtons({items, onPress, errorText}: RadioButtonsProps, ref: ForwardedRef<View>) { + console.log('RORY_DEBUG errorText', errorText); const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(''); return ( <> - <View style={styles.mb3}> + <View + style={styles.mb3} + ref={ref} + > {items.map((item) => ( <RadioButtonWithLabel key={item.value} diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index f4fb87134be1..189fca480654 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -69,7 +69,6 @@ function ExitSurveyReasonPage() { items={reasons} onPress={(value) => setReason(value as Reason)} /> - x </> </FormProvider> </ScreenWrapper> From 8c909c17cae0e26f3ed4eacdc1381fd673cdb6d9 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 10:19:55 -0800 Subject: [PATCH 36/81] Clear onyx form data after sending to API --- src/libs/actions/ExitSurvey.ts | 36 ++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index 766d0463257a..7e7234bc754e 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -1,3 +1,4 @@ +import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; @@ -33,10 +34,37 @@ function switchToOldDot() { return; } - API.write('SwitchToOldDot', { - reason: exitReason, - surveyResponse: exitSurveyResponse, - }); + const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, + value: null, + }, + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT, + value: null, + }, + ]; + + API.write( + 'SwitchToOldDot', + { + reason: exitReason, + surveyResponse: exitSurveyResponse, + }, + {finallyData}, + ); } export {saveExitReason, saveResponse, switchToOldDot}; From 639afcd9818898e48c36011eea3a54158afa346c Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 10:56:15 -0800 Subject: [PATCH 37/81] Attempt to apply drafts - seeing cryptic type error --- src/ONYXKEYS.ts | 2 +- src/libs/FormUtils.ts | 2 +- .../ExitSurvey/ExitSurveyResponsePage.tsx | 17 ++++++++++++++--- src/types/onyx/Form.ts | 4 ++++ src/types/onyx/index.ts | 2 ++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index d25962508efe..86501d6e27f2 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -556,7 +556,7 @@ type OnyxValues = { [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.ExitSurveyReasonForm; [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.ExitSurveyResponseForm; - [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.ExitSurveyResponseForm; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.FormDraft<OnyxTypes.ExitSurveyResponseForm>; }; type OnyxKeyValue<TOnyxKey extends (OnyxKey | OnyxCollectionKey) & keyof OnyxValues> = OnyxEntry<OnyxValues[TOnyxKey]>; diff --git a/src/libs/FormUtils.ts b/src/libs/FormUtils.ts index 37241df49af7..d99e5f2e93c9 100644 --- a/src/libs/FormUtils.ts +++ b/src/libs/FormUtils.ts @@ -1,6 +1,6 @@ import type {OnyxFormKeyWithoutDraft} from '@components/Form/types'; -function getDraftKey(formID: OnyxFormKeyWithoutDraft): `${OnyxFormKeyWithoutDraft}Draft` { +function getDraftKey<Key extends OnyxFormKeyWithoutDraft>(formID: Key): `${Key}Draft` { return `${formID}Draft`; } diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 2b4e6b4d51e0..4284c79df580 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,7 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useRef, useState} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -12,6 +14,7 @@ import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import FormUtils from '@libs/FormUtils'; import * as NumberUtils from '@libs/NumberUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; @@ -24,9 +27,13 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type {Errors} from '@src/types/onyx/OnyxCommon'; -type ExitSurveyResponsePageProps = StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; +type ExitSurveyResponsePageOnyxProps = { + responseDraft: OnyxEntry<string>; +}; -function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { +type ExitSurveyResponsePageProps = ExitSurveyResponsePageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; + +function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -120,4 +127,8 @@ function ExitSurveyResponsePage({route}: ExitSurveyResponsePageProps) { ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; -export default ExitSurveyResponsePage; +export default withOnyx<ExitSurveyResponsePageProps, ExitSurveyResponsePageOnyxProps>({ + responseDraft: { + key: FormUtils.getDraftKey(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM), + }, +})(ExitSurveyResponsePage); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 56736500713c..d99fad1fd0d3 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -17,6 +17,9 @@ type BaseForm = { }; type Form<TFormValues extends Record<string, FormValueType> = Record<string, FormValueType>> = TFormValues & BaseForm; +type FormDraft<TForm> = { + [Key in keyof TForm as `${Key & string}Draft`]: TForm[Key]; +}; type AddDebitCardForm = Form<{ /** Whether the form has been submitted */ @@ -82,4 +85,5 @@ export type { PersonalBankAccountForm, ExitSurveyReasonForm, ExitSurveyResponseForm, + FormDraft, }; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 8f7afa24f03d..02ff9ba20387 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -15,6 +15,7 @@ import type { DisplayNameForm, ExitSurveyReasonForm, ExitSurveyResponseForm, + FormDraft, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, @@ -97,6 +98,7 @@ export type { ExitSurveyReasonForm, ExitSurveyResponseForm, Form, + FormDraft, FrequentlyUsedEmoji, Fund, FundList, From 1ee4118034faec2aca98734ee315e33b4dc96af2 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 13:16:00 -0800 Subject: [PATCH 38/81] Populate response TextInput with draft response --- src/ONYXKEYS.ts | 2 +- .../ExitSurvey/ExitSurveyResponsePage.tsx | 19 ++++++++++--------- src/types/onyx/Form.ts | 4 ---- src/types/onyx/index.ts | 2 -- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 86501d6e27f2..d25962508efe 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -556,7 +556,7 @@ type OnyxValues = { [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: OnyxTypes.ExitSurveyReasonForm; [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT]: OnyxTypes.Form; [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: OnyxTypes.ExitSurveyResponseForm; - [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.FormDraft<OnyxTypes.ExitSurveyResponseForm>; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT]: OnyxTypes.ExitSurveyResponseForm; }; type OnyxKeyValue<TOnyxKey extends (OnyxKey | OnyxCollectionKey) & keyof OnyxValues> = OnyxEntry<OnyxValues[TOnyxKey]>; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 4284c79df580..e79b57cea787 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useRef, useState} from 'react'; +import React, {useRef} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -25,15 +25,16 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; type ExitSurveyResponsePageOnyxProps = { - responseDraft: OnyxEntry<string>; + draftResponse: string; }; type ExitSurveyResponsePageProps = ExitSurveyResponsePageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; -function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePageProps) { +function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -42,7 +43,6 @@ function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePagePr const {reason} = route.params; - const [response, setResponse] = useState(''); const responseInputRef = useRef<AnimatedTextInputRef | null>(null); const formTopMarginsStyle = styles.mt3; @@ -83,13 +83,13 @@ function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePagePr formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} onSubmit={() => { - ExitSurvey.saveResponse(response); + ExitSurvey.saveResponse(draftResponse); Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM); }} submitButtonText={translate('common.next')} validate={() => { const errors: Errors = {}; - if (!response?.trim()) { + if (!draftResponse?.trim()) { errors[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID] = 'common.error.fieldRequired'; } return errors; @@ -114,8 +114,8 @@ function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePagePr responseInputRef.current = el; updateMultilineInputRange(el); }} - value={response} - onChangeText={setResponse} + value={draftResponse} + // onChangeText={setResponse} containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} shouldSaveDraft /> @@ -128,7 +128,8 @@ function ExitSurveyResponsePage({responseDraft, route}: ExitSurveyResponsePagePr ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; export default withOnyx<ExitSurveyResponsePageProps, ExitSurveyResponsePageOnyxProps>({ - responseDraft: { + draftResponse: { key: FormUtils.getDraftKey(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM), + selector: (value: OnyxEntry<OnyxTypes.ExitSurveyResponseForm>) => value?.response ?? '', }, })(ExitSurveyResponsePage); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index d99fad1fd0d3..56736500713c 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -17,9 +17,6 @@ type BaseForm = { }; type Form<TFormValues extends Record<string, FormValueType> = Record<string, FormValueType>> = TFormValues & BaseForm; -type FormDraft<TForm> = { - [Key in keyof TForm as `${Key & string}Draft`]: TForm[Key]; -}; type AddDebitCardForm = Form<{ /** Whether the form has been submitted */ @@ -85,5 +82,4 @@ export type { PersonalBankAccountForm, ExitSurveyReasonForm, ExitSurveyResponseForm, - FormDraft, }; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index 02ff9ba20387..8f7afa24f03d 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -15,7 +15,6 @@ import type { DisplayNameForm, ExitSurveyReasonForm, ExitSurveyResponseForm, - FormDraft, IKnowATeacherForm, IntroSchoolPrincipalForm, NewRoomForm, @@ -98,7 +97,6 @@ export type { ExitSurveyReasonForm, ExitSurveyResponseForm, Form, - FormDraft, FrequentlyUsedEmoji, Fund, FundList, From 41457dcd72725e57a3d9176cf6b59337bacdca07 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 13:21:09 -0800 Subject: [PATCH 39/81] Use Onyx.set instead of Onyx.merge --- src/libs/actions/ExitSurvey.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index 7e7234bc754e..a21e4a4d35a5 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -18,11 +18,11 @@ Onyx.connect({ }); function saveExitReason(reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) { - Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); + Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); } function saveResponse(response: string) { - Onyx.merge(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: response}); + Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: response}); } /** From f738173a0f7356a385b490e6456dd2eb4e132031 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 13:23:06 -0800 Subject: [PATCH 40/81] Remove errant console.log --- src/components/RadioButtons.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index 12447b1cb97d..6e9ca90a0d89 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -25,7 +25,6 @@ type RadioButtonsProps = { }; function RadioButtons({items, onPress, errorText}: RadioButtonsProps, ref: ForwardedRef<View>) { - console.log('RORY_DEBUG errorText', errorText); const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(''); From 0faf0c53166ec2e25acdc3ba805d9249841d3b7b Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 31 Jan 2024 21:43:05 -0800 Subject: [PATCH 41/81] Implement loading state in confirm page --- src/libs/actions/ExitSurvey.ts | 7 +++-- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 30 +++++++++++++++++-- .../ExitSurvey/ExitSurveyReasonPage.tsx | 8 ++--- .../ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- src/types/onyx/Form.ts | 4 +-- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index a21e4a4d35a5..063095699057 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -6,7 +6,9 @@ import Log from '@libs/Log'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -let exitReason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS> | undefined; +type ExitReason = ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + +let exitReason: ExitReason | undefined; let exitSurveyResponse: string | undefined; Onyx.connect({ key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, @@ -17,7 +19,7 @@ Onyx.connect({ callback: (value) => (exitSurveyResponse = value?.[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]), }); -function saveExitReason(reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) { +function saveExitReason(reason: ExitReason) { Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); } @@ -68,3 +70,4 @@ function switchToOldDot() { } export {saveExitReason, saveResponse, switchToOldDot}; +export type {ExitReason}; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index e03515ab3742..7a6b7eb147dc 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,5 +1,7 @@ -import React from 'react'; +import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -10,13 +12,28 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; +import type {ExitReason} from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type * as OnyxTypes from '@src/types/onyx'; -function ExitSurveyConfirmPage() { +type ExitSurveyConfirmPageOnyxProps = { + exitReason: ExitReason | null; +}; + +function ExitSurveyConfirmPage({exitReason}: ExitSurveyConfirmPageOnyxProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const [isLoading, setIsLoading] = useState(false); + useEffect(() => { + if (exitReason) { + return; + } + setIsLoading(false); + }, [exitReason]); + return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton @@ -33,9 +50,11 @@ function ExitSurveyConfirmPage() { success text={translate('exitSurvey.goToExpensifyClassic')} onPress={() => { + setIsLoading(true); ExitSurvey.switchToOldDot(); Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); }} + isLoading={isLoading} /> </FixedFooter> </ScreenWrapper> @@ -44,4 +63,9 @@ function ExitSurveyConfirmPage() { ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; -export default ExitSurveyConfirmPage; +export default withOnyx<ExitSurveyConfirmPageOnyxProps, ExitSurveyConfirmPageOnyxProps>({ + exitReason: { + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, + selector: (value: OnyxEntry<OnyxTypes.ExitSurveyReasonForm>) => value?.reason ?? null, + }, +})(ExitSurveyConfirmPage); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 189fca480654..e0df8957ef91 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,5 +1,4 @@ import React, {useMemo, useState} from 'react'; -import type {ValueOf} from 'type-fest'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -11,18 +10,17 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; +import type {ExitReason} from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Errors} from '@src/types/onyx/OnyxCommon'; -type Reason = ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; - function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - const [reason, setReason] = useState<Reason>(); + const [reason, setReason] = useState<ExitReason>(); const reasons: Choice[] = useMemo( () => Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ @@ -67,7 +65,7 @@ function ExitSurveyReasonPage() { InputComponent={RadioButtons} inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} items={reasons} - onPress={(value) => setReason(value as Reason)} + onPress={(value) => setReason(value as ExitReason)} /> </> </FormProvider> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index e79b57cea787..291afe6370c5 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -130,6 +130,6 @@ ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; export default withOnyx<ExitSurveyResponsePageProps, ExitSurveyResponsePageOnyxProps>({ draftResponse: { key: FormUtils.getDraftKey(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM), - selector: (value: OnyxEntry<OnyxTypes.ExitSurveyResponseForm>) => value?.response ?? '', + selector: (value: OnyxEntry<OnyxTypes.ExitSurveyResponseForm>) => value?.[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID] ?? '', }, })(ExitSurveyResponsePage); diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts index 56736500713c..82800a04ac40 100644 --- a/src/types/onyx/Form.ts +++ b/src/types/onyx/Form.ts @@ -1,4 +1,4 @@ -import type {ValueOf} from 'type-fest'; +import type {ExitReason} from '@userActions/ExitSurvey'; import type CONST from '@src/CONST'; import type * as OnyxCommon from './OnyxCommon'; import type PersonalBankAccount from './PersonalBankAccount'; @@ -60,7 +60,7 @@ type PrivateNotesForm = Form<{ type PersonalBankAccountForm = Form<PersonalBankAccount>; type ExitSurveyReasonForm = Form<{ - [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ExitReason; }>; type ExitSurveyResponseForm = Form<{ From ef706f9285a0cb6d4224ddfd96eea05064f249cc Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 15:23:03 -0800 Subject: [PATCH 42/81] Fix linking config --- .../AppNavigator/ModalStackNavigators.tsx | 6 +++--- src/libs/Navigation/linkingConfig/config.ts | 9 +++++++++ src/pages/settings/InitialSettingsPage.js | 14 +++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index f9a37a810e11..b17ba463e480 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -191,6 +191,9 @@ const AccountSettingsModalStackNavigator = createModalStackNavigator( [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType, }, (styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false}), ); @@ -245,9 +248,6 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: () => require('../../../pages/settings/Wallet/ReportCardLostPage').default as React.ComponentType, [SCREENS.KEYBOARD_SHORTCUTS]: () => require('../../../pages/KeyboardShortcutsPage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType, }); const EnablePaymentsStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({ diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index f1c9c316fe93..c0db821e8886 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -505,6 +505,15 @@ const config: LinkingOptions<RootStackParamList>['config'] = { path: ROUTES.SETTINGS_ABOUT, exact: true, }, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_REASON, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM, + }, }, }, }, diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 8f3450915bfa..560d7a73e310 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -165,13 +165,6 @@ function InitialSettingsPage(props) { icon: Expensicons.Lock, routeName: ROUTES.SETTINGS_SECURITY, }, - { - translationKey: 'initialSettingsPage.goToExpensifyClassic', - icon: Expensicons.NewExpensify, - action: () => { - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); - }, - }, { translationKey: 'initialSettingsPage.signOut', icon: Expensicons.Exit, @@ -207,6 +200,13 @@ function InitialSettingsPage(props) { icon: Expensicons.Info, routeName: ROUTES.SETTINGS_ABOUT, }, + { + translationKey: 'initialSettingsPage.goToExpensifyClassic', + icon: Expensicons.NewExpensify, + action: () => { + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); + }, + }, ], }), [styles.pt4], From 8fab4f9c9de907de0872cbe655e9c92d1f8d27b5 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 15:25:30 -0800 Subject: [PATCH 43/81] Consolidate goToExpensifyClassic copy --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - src/pages/settings/InitialSettingsPage.js | 14 +++++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 954e6ac25bb1..9bd4f7c9cc83 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -806,7 +806,6 @@ export default { label: 'macOS', }, }, - goToExpensifyClassic: 'Go to Expensify Classic', security: 'Security', signOut: 'Sign out', signOutConfirmationText: "You'll lose any offline changes if you sign-out.", diff --git a/src/languages/es.ts b/src/languages/es.ts index e61b6802d880..05b6738663d6 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -804,7 +804,6 @@ export default { signOut: 'Desconectar', signOutConfirmationText: 'Si cierras sesión perderás los cambios hechos mientras estabas desconectado', versionLetter: 'v', - goToExpensifyClassic: 'Ir a Expensify Classic', readTheTermsAndPrivacy: { phrase1: 'Leer los', phrase2: 'Términos de Servicio', diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 560d7a73e310..5def717d5e3f 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -187,6 +187,13 @@ function InitialSettingsPage(props) { }, sectionTranslationKey: 'initialSettingsPage.general', items: [ + { + translationKey: 'exitSurvey.goToExpensifyClassic', + icon: Expensicons.NewExpensify, + action: () => { + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); + }, + }, { translationKey: 'initialSettingsPage.help', icon: Expensicons.QuestionMark, @@ -200,13 +207,6 @@ function InitialSettingsPage(props) { icon: Expensicons.Info, routeName: ROUTES.SETTINGS_ABOUT, }, - { - translationKey: 'initialSettingsPage.goToExpensifyClassic', - icon: Expensicons.NewExpensify, - action: () => { - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); - }, - }, ], }), [styles.pt4], From 222cfb65d7f34ba3f883f39ce74cdadef10c9a7f Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 15:26:22 -0800 Subject: [PATCH 44/81] Upgrade react-native-onyx --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index eea17b8ea1e4..14662e7a2bc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.1", + "react-native-onyx": "2.0.4", "react-native-pager-view": "6.2.2", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", @@ -45056,9 +45056,9 @@ } }, "node_modules/react-native-onyx": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.1.tgz", - "integrity": "sha512-o6QNvq91qg8hFXIhmHjBqlNXD/YZxBZSRN8Vkq7xD2NYskzxK2mLqhBdhB8yMMwe6Cd8sVUK4vlZax/JU79xYw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.4.tgz", + "integrity": "sha512-QpeqqpvH4XGTWgPK4qJ6NoBvHDXFWtbLOuGBN7iqd3RVFx+juxgiuaf+Zr0GEpR2P40vsX5IA0m1SiAotvzbmA==", "dependencies": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", diff --git a/package.json b/package.json index 0e8e08aa187f..997b44fc5d94 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.1", + "react-native-onyx": "2.0.4", "react-native-pager-view": "6.2.2", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", From 33d614d2b6851d88aca0045887e13cacb01b8fa8 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 16:10:04 -0800 Subject: [PATCH 45/81] Hide reason page back button on wide layouts --- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index e0df8957ef91..1663102aa0dd 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -8,6 +8,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; import type {ExitReason} from '@userActions/ExitSurvey'; @@ -19,6 +20,7 @@ import type {Errors} from '@src/types/onyx/OnyxCommon'; function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); + const {isSmallScreenWidth} = useWindowDimensions(); const [reason, setReason] = useState<ExitReason>(); const reasons: Choice[] = useMemo( @@ -36,6 +38,7 @@ function ExitSurveyReasonPage() { <HeaderWithBackButton title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} + shouldShowBackButton={isSmallScreenWidth} /> <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM} From db6b991ebef2dd48382c497c8694046bc03665d1 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 16:15:33 -0800 Subject: [PATCH 46/81] Remove unnecessary ref --- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 291afe6370c5..a6939e24277e 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useRef} from 'react'; +import React from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -43,8 +43,6 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr const {reason} = route.params; - const responseInputRef = useRef<AnimatedTextInputRef | null>(null); - const formTopMarginsStyle = styles.mt3; const textStyle = styles.headerAnonymousFooter; const baseResponseInputContainerStyle = styles.mt7; @@ -111,11 +109,9 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr if (!el) { return; } - responseInputRef.current = el; updateMultilineInputRange(el); }} value={draftResponse} - // onChangeText={setResponse} containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} shouldSaveDraft /> From 3bff7db2daaa84d35ad3d0002f1263df1080541a Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 16:20:21 -0800 Subject: [PATCH 47/81] Submit response form on CMD+Enter --- .../settings/ExitSurvey/ExitSurveyResponsePage.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index a6939e24277e..6b5f38d3d399 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React from 'react'; +import React, {useCallback} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; @@ -9,6 +9,7 @@ import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; +import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -43,6 +44,12 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr const {reason} = route.params; + const submitForm = useCallback(() => { + ExitSurvey.saveResponse(draftResponse); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM); + }, [draftResponse]); + useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.CTRL_ENTER, submitForm); + const formTopMarginsStyle = styles.mt3; const textStyle = styles.headerAnonymousFooter; const baseResponseInputContainerStyle = styles.mt7; @@ -80,10 +87,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} style={[styles.flex1, styles.mh5, formTopMarginsStyle, StyleUtils.getMaximumHeight(formMaxHeight)]} - onSubmit={() => { - ExitSurvey.saveResponse(draftResponse); - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM); - }} + onSubmit={submitForm} submitButtonText={translate('common.next')} validate={() => { const errors: Errors = {}; From a7f4a2f5bd11bb8f6a1c993cfdac9d1adac50ed6 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 16:50:14 -0800 Subject: [PATCH 48/81] Fix form second submission --- src/ONYXKEYS.ts | 4 +++ .../API/parameters/SwitchToOldDotParams.ts | 4 +-- src/libs/actions/ExitSurvey.ts | 19 +++++++++----- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 25 +++++-------------- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 752060b93507..713d08f0375a 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -191,6 +191,9 @@ const ONYXKEYS = { /** Is report data loading? */ IS_LOADING_APP: 'isLoadingApp', + /** Is the user in the process of switching to OldDot? */ + IS_SWITCHING_TO_OLD_DOT: 'isSwitchingToOldDot', + /** Is the test tools modal open? */ IS_TEST_TOOLS_MODAL_OPEN: 'isTestToolsModalOpen', @@ -439,6 +442,7 @@ type OnyxValues = { [ONYXKEYS.IS_LOADING_REPORT_DATA]: boolean; [ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN]: boolean; [ONYXKEYS.IS_LOADING_APP]: boolean; + [ONYXKEYS.IS_SWITCHING_TO_OLD_DOT]: boolean; [ONYXKEYS.WALLET_TRANSFER]: OnyxTypes.WalletTransfer; [ONYXKEYS.LAST_ACCESSED_WORKSPACE_POLICY_ID]: string; [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: boolean; diff --git a/src/libs/API/parameters/SwitchToOldDotParams.ts b/src/libs/API/parameters/SwitchToOldDotParams.ts index 7fe363a542e4..95449a123dc9 100644 --- a/src/libs/API/parameters/SwitchToOldDotParams.ts +++ b/src/libs/API/parameters/SwitchToOldDotParams.ts @@ -2,8 +2,8 @@ import type {ValueOf} from 'type-fest'; import type CONST from '@src/CONST'; type SwitchToOldDotParams = { - reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; - surveyResponse: string; + reason?: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + surveyResponse?: string; }; export default SwitchToOldDotParams; diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index 063095699057..7b3ba839397c 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -2,7 +2,6 @@ import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; -import Log from '@libs/Log'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -31,12 +30,20 @@ function saveResponse(response: string) { * Save the user's response to the mandatory exit survey in the back-end. */ function switchToOldDot() { - if (!exitReason || !exitSurveyResponse) { - Log.hmmm('Attempted to call SwitchToOldDot without filling out mandatory survey.'); - return; - } + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT, + value: true, + }, + ]; const finallyData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.SET, + key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT, + value: false, + }, { onyxMethod: Onyx.METHOD.SET, key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, @@ -65,7 +72,7 @@ function switchToOldDot() { reason: exitReason, surveyResponse: exitSurveyResponse, }, - {finallyData}, + {optimisticData, finallyData}, ); } diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 7a6b7eb147dc..c347029b3a77 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from 'react'; +import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -12,28 +12,17 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; -import type {ExitReason} from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type * as OnyxTypes from '@src/types/onyx'; type ExitSurveyConfirmPageOnyxProps = { - exitReason: ExitReason | null; + isLoading: OnyxEntry<boolean>; }; -function ExitSurveyConfirmPage({exitReason}: ExitSurveyConfirmPageOnyxProps) { +function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - - const [isLoading, setIsLoading] = useState(false); - useEffect(() => { - if (exitReason) { - return; - } - setIsLoading(false); - }, [exitReason]); - return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton @@ -50,11 +39,10 @@ function ExitSurveyConfirmPage({exitReason}: ExitSurveyConfirmPageOnyxProps) { success text={translate('exitSurvey.goToExpensifyClassic')} onPress={() => { - setIsLoading(true); ExitSurvey.switchToOldDot(); Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); }} - isLoading={isLoading} + isLoading={isLoading ?? false} /> </FixedFooter> </ScreenWrapper> @@ -64,8 +52,7 @@ function ExitSurveyConfirmPage({exitReason}: ExitSurveyConfirmPageOnyxProps) { ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; export default withOnyx<ExitSurveyConfirmPageOnyxProps, ExitSurveyConfirmPageOnyxProps>({ - exitReason: { - key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, - selector: (value: OnyxEntry<OnyxTypes.ExitSurveyReasonForm>) => value?.reason ?? null, + isLoading: { + key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT, }, })(ExitSurveyConfirmPage); From a61b8d52ecfe59dc62997b70bad34f3791b4ccf5 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 17:04:06 -0800 Subject: [PATCH 49/81] Make isOffline a true boolean --- src/hooks/useNetwork.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts index f9e1a627c5f5..eae7c53c7173 100644 --- a/src/hooks/useNetwork.ts +++ b/src/hooks/useNetwork.ts @@ -5,7 +5,7 @@ type UseNetworkProps = { onReconnect?: () => void; }; -type UseNetwork = {isOffline?: boolean}; +type UseNetwork = {isOffline: boolean}; export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = {}): UseNetwork { const callback = useRef(onReconnect); @@ -28,5 +28,5 @@ export default function useNetwork({onReconnect = () => {}}: UseNetworkProps = { prevOfflineStatusRef.current = isOffline; }, [isOffline]); - return {isOffline}; + return {isOffline: isOffline ?? false}; } From 566000b04c4afdd1a20735982419df2dfe853fe8 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 17:50:28 -0800 Subject: [PATCH 50/81] Implement offline UI for reason page --- src/languages/en.ts | 1 + .../ExitSurvey/ExitSurveyReasonPage.tsx | 39 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 9bd4f7c9cc83..6d55887789e4 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2174,6 +2174,7 @@ export default { thankYou: 'Thanks for the feedback!', thankYouSubtitle: 'Your responses will help us build a better product to get stuff done. Thank you so much!', goToExpensifyClassic: 'Switch to Expensify Classic', + offlineTitle: "Looks like you're stuck here...", offline: "You appear to be offline. Unfortunately, Expensify Classic doesn't work offline, but New Expensify does. If you prefer to use Expensify Classic, try again when you have an internet connection.", }, diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 1663102aa0dd..7a34c386c56b 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,15 +1,20 @@ import React, {useMemo, useState} from 'react'; +import {View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import {ToddBehindCloud} from '@components/Icon/Illustrations'; import type {Choice} from '@components/RadioButtons'; import RadioButtons from '@components/RadioButtons'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@navigation/Navigation'; +import variables from '@styles/variables'; import * as ExitSurvey from '@userActions/ExitSurvey'; import type {ExitReason} from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; @@ -21,6 +26,7 @@ function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); const {isSmallScreenWidth} = useWindowDimensions(); + const {isOffline} = useNetwork(); const [reason, setReason] = useState<ExitReason>(); const reasons: Choice[] = useMemo( @@ -61,16 +67,29 @@ function ExitSurveyReasonPage() { shouldValidateOnBlur shouldValidateOnChange > - <> - <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> - <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> - <InputWrapper - InputComponent={RadioButtons} - inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} - items={reasons} - onPress={(value) => setReason(value as ExitReason)} - /> - </> + {isOffline && ( + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter]}> + <Icon + width={variables.modalTopIconWidth} + height={variables.modalTopIconHeight} + src={ToddBehindCloud} + /> + <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.offlineTitle')}</Text> + <Text style={styles.mt2}>{translate('exitSurvey.offline')}</Text> + </View> + )} + {!isOffline && ( + <> + <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> + <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> + <InputWrapper + InputComponent={RadioButtons} + inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} + items={reasons} + onPress={(value) => setReason(value as ExitReason)} + /> + </> + )} </FormProvider> </ScreenWrapper> ); From b3245a333ba4f85960503ca4e6b62ed6880ef443 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 18:02:48 -0800 Subject: [PATCH 51/81] Implement offline UI for the other pages in the survey as well --- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 27 ++++++++--- .../settings/ExitSurvey/ExitSurveyOffline.tsx | 28 +++++++++++ .../ExitSurvey/ExitSurveyReasonPage.tsx | 17 +------ .../ExitSurvey/ExitSurveyResponsePage.tsx | 48 +++++++++++-------- 4 files changed, 78 insertions(+), 42 deletions(-) create mode 100644 src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index c347029b3a77..c7d6088a2b97 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -9,12 +9,15 @@ import {MushroomTopHat} from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyConfirmPageOnyxProps = { isLoading: OnyxEntry<boolean>; @@ -22,18 +25,30 @@ type ExitSurveyConfirmPageOnyxProps = { function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { const {translate} = useLocalize(); + const {isOffline} = useNetwork(); const styles = useThemeStyles(); + + useEffect(() => { + if (!isOffline) { + return; + } + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); + }, [isOffline]); + return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} /> - <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> - <MushroomTopHat /> - <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> - <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> - </View> + {isOffline && <ExitSurveyOffline />} + {!isOffline && ( + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> + <MushroomTopHat /> + <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> + <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> + </View> + )} <FixedFooter> <Button success diff --git a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx new file mode 100644 index 000000000000..a02ee48d1943 --- /dev/null +++ b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx @@ -0,0 +1,28 @@ +import React, {memo} from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import {ToddBehindCloud} from '@components/Icon/Illustrations'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; + +function ExitSurveyOffline() { + const {translate} = useLocalize(); + const styles = useThemeStyles(); + return ( + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter]}> + <Icon + width={variables.modalTopIconWidth} + height={variables.modalTopIconHeight} + src={ToddBehindCloud} + /> + <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.offlineTitle')}</Text> + <Text style={styles.mt2}>{translate('exitSurvey.offline')}</Text> + </View> + ); +} + +ExitSurveyOffline.displayName = 'ExitSurveyOffline'; + +export default memo(ExitSurveyOffline); diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 7a34c386c56b..0ee59fe88bbd 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,10 +1,7 @@ import React, {useMemo, useState} from 'react'; -import {View} from 'react-native'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; -import Icon from '@components/Icon'; -import {ToddBehindCloud} from '@components/Icon/Illustrations'; import type {Choice} from '@components/RadioButtons'; import RadioButtons from '@components/RadioButtons'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -14,13 +11,13 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@navigation/Navigation'; -import variables from '@styles/variables'; import * as ExitSurvey from '@userActions/ExitSurvey'; import type {ExitReason} from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Errors} from '@src/types/onyx/OnyxCommon'; +import ExitSurveyOffline from './ExitSurveyOffline'; function ExitSurveyReasonPage() { const {translate} = useLocalize(); @@ -67,17 +64,7 @@ function ExitSurveyReasonPage() { shouldValidateOnBlur shouldValidateOnChange > - {isOffline && ( - <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter]}> - <Icon - width={variables.modalTopIconWidth} - height={variables.modalTopIconHeight} - src={ToddBehindCloud} - /> - <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.offlineTitle')}</Text> - <Text style={styles.mt2}>{translate('exitSurvey.offline')}</Text> - </View> - )} + {isOffline && <ExitSurveyOffline />} {!isOffline && ( <> <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.reasonPage.title')}</Text> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 6b5f38d3d399..ce0b81cdabff 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -11,6 +11,7 @@ import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -28,6 +29,7 @@ import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; import type {Errors} from '@src/types/onyx/OnyxCommon'; +import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyResponsePageOnyxProps = { draftResponse: string; @@ -37,6 +39,7 @@ type ExitSurveyResponsePageProps = ExitSurveyResponsePageOnyxProps & StackScreen function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); + const {isOffline} = useNetwork(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {windowHeight} = useWindowDimensions(); @@ -99,27 +102,30 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr shouldValidateOnBlur shouldValidateOnChange > - <> - <Text style={textStyle}>{translate(`exitSurvey.prompts.${reason}`)}</Text> - <InputWrapper - InputComponent={TextInput} - inputID={CONST.EXIT_SURVEY.RESPONSE_INPUT_ID} - label={translate(`exitSurvey.responsePlaceholder`)} - accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} - role={CONST.ROLE.PRESENTATION} - autoGrowHeight - maxLength={CONST.MAX_COMMENT_LENGTH} - ref={(el: AnimatedTextInputRef) => { - if (!el) { - return; - } - updateMultilineInputRange(el); - }} - value={draftResponse} - containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} - shouldSaveDraft - /> - </> + {isOffline && <ExitSurveyOffline />} + {!isOffline && ( + <> + <Text style={textStyle}>{translate(`exitSurvey.prompts.${reason}`)}</Text> + <InputWrapper + InputComponent={TextInput} + inputID={CONST.EXIT_SURVEY.RESPONSE_INPUT_ID} + label={translate(`exitSurvey.responsePlaceholder`)} + accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} + role={CONST.ROLE.PRESENTATION} + autoGrowHeight + maxLength={CONST.MAX_COMMENT_LENGTH} + ref={(el: AnimatedTextInputRef) => { + if (!el) { + return; + } + updateMultilineInputRange(el); + }} + value={draftResponse} + containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} + shouldSaveDraft + /> + </> + )} </FormProvider> </ScreenWrapper> ); From 220a974a3dad4fd27ee97785c29e1db1ee199f3b Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 1 Feb 2024 18:03:39 -0800 Subject: [PATCH 52/81] Disable button in confirm page when offline --- src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index c7d6088a2b97..932f8d2803aa 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -58,6 +58,7 @@ function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); }} isLoading={isLoading ?? false} + isDisabled={isOffline} /> </FixedFooter> </ScreenWrapper> From aed59f1591a9c9733c45cd2259d4b24b0744680d Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 10:34:30 -0800 Subject: [PATCH 53/81] Move survey back to RHP --- src/libs/Navigation/linkingConfig/config.ts | 18 +++++++++--------- src/pages/settings/InitialSettingsPage.js | 4 +--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index c0db821e8886..496588d8114d 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -250,6 +250,15 @@ const config: LinkingOptions<RootStackParamList>['config'] = { path: ROUTES.KEYBOARD_SHORTCUTS, }, [SCREENS.WORKSPACE.NAME]: ROUTES.WORKSPACE_OVERVIEW_NAME.route, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_REASON, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, + }, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { + path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM, + }, }, }, [SCREENS.RIGHT_MODAL.PRIVATE_NOTES]: { @@ -505,15 +514,6 @@ const config: LinkingOptions<RootStackParamList>['config'] = { path: ROUTES.SETTINGS_ABOUT, exact: true, }, - [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_REASON, - }, - [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, - }, - [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM, - }, }, }, }, diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 5def717d5e3f..a5417ce9d4e3 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -190,9 +190,7 @@ function InitialSettingsPage(props) { { translationKey: 'exitSurvey.goToExpensifyClassic', icon: Expensicons.NewExpensify, - action: () => { - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); - }, + routeName: ROUTES.SETTINGS_EXIT_SURVEY_REASON, }, { translationKey: 'initialSettingsPage.help', From 4222758f2d61c5980acfd027fbe838952bbfe9e9 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 10:35:59 -0800 Subject: [PATCH 54/81] Fix typo --- src/pages/settings/InitialSettingsPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index a5417ce9d4e3..7acbd8fedfad 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -180,7 +180,7 @@ function InitialSettingsPage(props) { * Retuns a list of menu items data for general section * @returns {Object} object with translationKey, style and items for the general section */ - const generaltMenuItemsData = useMemo( + const generalMenuItemsData = useMemo( () => ({ sectionStyle: { ...styles.pt4, @@ -283,7 +283,7 @@ function InitialSettingsPage(props) { ); const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); - const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); + const generalMenuItems = useMemo(() => getMenuItemsSection(generalMenuItemsData), [generalMenuItemsData, getMenuItemsSection]); const currentUserDetails = props.currentUserPersonalDetails || {}; const avatarURL = lodashGet(currentUserDetails, 'avatar', ''); From edbf8a4125bd3a57d32cd95eb18713f9e2506b9e Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 10:53:40 -0800 Subject: [PATCH 55/81] Fix navigation to RHP --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 6 +++--- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index b17ba463e480..f9a37a810e11 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -191,9 +191,6 @@ const AccountSettingsModalStackNavigator = createModalStackNavigator( [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType, - [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType, }, (styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false}), ); @@ -248,6 +245,9 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, [SCREENS.SETTINGS.REPORT_CARD_LOST_OR_DAMAGED]: () => require('../../../pages/settings/Wallet/ReportCardLostPage').default as React.ComponentType, [SCREENS.KEYBOARD_SHORTCUTS]: () => require('../../../pages/KeyboardShortcutsPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyReasonPage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyResponsePage').default as React.ComponentType, + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: () => require('../../../pages/settings/ExitSurvey/ExitSurveyConfirmPage').default as React.ComponentType, }); const EnablePaymentsStackNavigator = createModalStackNavigator<EnablePaymentsNavigatorParamList>({ diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 0ee59fe88bbd..ea19d58c3b9f 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -9,7 +9,6 @@ import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import useWindowDimensions from '@hooks/useWindowDimensions'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; import type {ExitReason} from '@userActions/ExitSurvey'; @@ -22,7 +21,6 @@ import ExitSurveyOffline from './ExitSurveyOffline'; function ExitSurveyReasonPage() { const {translate} = useLocalize(); const styles = useThemeStyles(); - const {isSmallScreenWidth} = useWindowDimensions(); const {isOffline} = useNetwork(); const [reason, setReason] = useState<ExitReason>(); @@ -41,7 +39,6 @@ function ExitSurveyReasonPage() { <HeaderWithBackButton title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack()} - shouldShowBackButton={isSmallScreenWidth} /> <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM} From 7c5e3317edbd5bc98a7fe4ef2e05a60908208b27 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 10:55:29 -0800 Subject: [PATCH 56/81] Remove unnecessary offline navigation effect --- .../settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 932f8d2803aa..19679fdcccdc 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -16,7 +16,6 @@ import * as ExitSurvey from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyConfirmPageOnyxProps = { @@ -27,14 +26,6 @@ function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); - - useEffect(() => { - if (!isOffline) { - return; - } - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_REASON); - }, [isOffline]); - return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton From 1893e7a498c12b136557d2d3a7082fdaffbba264 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 11:25:14 -0800 Subject: [PATCH 57/81] Add backTo param to response and confirm page, even though it doesn't work --- src/ROUTES.ts | 7 +++++-- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 2 +- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index c38cb34adaac..cc623c44475f 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -158,9 +158,12 @@ const ROUTES = { SETTINGS_EXIT_SURVEY_REASON: 'settings/exit-survey/reason', SETTINGS_EXIT_SURVEY_RESPONSE: { route: 'settings/exit-survey/response', - getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>) => `settings/exit-survey/response?reason=${encodeURIComponent(reason)}` as const, + getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>, backTo: string) => getUrlWithBackToParam(`settings/exit-survey/response?reason=${encodeURIComponent(reason)}`, backTo), + }, + SETTINGS_EXIT_SURVEY_CONFIRM: { + route: 'settings/exit-survey/confirm', + getRoute: (backTo: string) => getUrlWithBackToParam('settings/exit-survey/confirm', backTo), }, - SETTINGS_EXIT_SURVEY_CONFIRM: 'settings/exit-survey/confirm', KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 496588d8114d..b02afb43f8b3 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -257,7 +257,7 @@ const config: LinkingOptions<RootStackParamList>['config'] = { path: ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route, }, [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { - path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM, + path: ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.route, }, }, }, diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index ea19d58c3b9f..acd55210af6f 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -55,7 +55,7 @@ function ExitSurveyReasonPage() { return; } ExitSurvey.saveExitReason(reason); - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason)); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(reason, ROUTES.SETTINGS_EXIT_SURVEY_REASON)); }} submitButtonText={translate('common.next')} shouldValidateOnBlur diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index ce0b81cdabff..e9ed2d3d16c9 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -49,7 +49,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr const submitForm = useCallback(() => { ExitSurvey.saveResponse(draftResponse); - Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM); + Navigation.navigate(ROUTES.SETTINGS_EXIT_SURVEY_CONFIRM.getRoute(ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.route)); }, [draftResponse]); useKeyboardShortcut(CONST.KEYBOARD_SHORTCUTS.CTRL_ENTER, submitForm); From ecfc373a84788f1eef3bdae59c14acebc4f5c643 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 11:25:26 -0800 Subject: [PATCH 58/81] Add missing spanish translation --- src/languages/es.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 05b6738663d6..0fe35d4a4902 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2662,6 +2662,7 @@ export default { thankYou: '¡Gracias por tus comentarios!', thankYouSubtitle: 'Sus respuestas nos ayudarán a crear un mejor producto para hacer las cosas bien. ¡Muchas gracias!', goToExpensifyClassic: 'Cambiar a Expensify Classic', + offlineTitle: 'Parece que estás atrapado aquí...', offline: 'Parece que estás desconectado. Desafortunadamente, Expensify Classic no funciona sin conexión, pero New Expensify sí. Si prefieres utilizar Expensify Classic, inténtalo de nuevo cuando tengas conexión a internet.', }, From fb01bd23b3e1f7e860ae05e976b277a246238576 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 11:57:01 -0800 Subject: [PATCH 59/81] Fix backTo bug --- src/ROUTES.ts | 5 +++-- .../settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 12 ++++++++++-- .../settings/ExitSurvey/ExitSurveyResponsePage.tsx | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index cc623c44475f..906786b7282d 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -158,11 +158,12 @@ const ROUTES = { SETTINGS_EXIT_SURVEY_REASON: 'settings/exit-survey/reason', SETTINGS_EXIT_SURVEY_RESPONSE: { route: 'settings/exit-survey/response', - getRoute: (reason: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>, backTo: string) => getUrlWithBackToParam(`settings/exit-survey/response?reason=${encodeURIComponent(reason)}`, backTo), + getRoute: (reason?: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>, backTo?: string) => + getUrlWithBackToParam(`settings/exit-survey/response${reason ? `?reason=${encodeURIComponent(reason)}` : ''}`, backTo), }, SETTINGS_EXIT_SURVEY_CONFIRM: { route: 'settings/exit-survey/confirm', - getRoute: (backTo: string) => getUrlWithBackToParam('settings/exit-survey/confirm', backTo), + getRoute: (backTo?: string) => getUrlWithBackToParam('settings/exit-survey/confirm', backTo), }, KEYBOARD_SHORTCUTS: 'keyboard-shortcuts', diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 19679fdcccdc..c7d8885c1944 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -13,16 +13,20 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; +import type {ExitReason} from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyConfirmPageOnyxProps = { + exitReason?: ExitReason; isLoading: OnyxEntry<boolean>; }; -function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { +function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOnyxProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); @@ -30,7 +34,7 @@ function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton title={translate('exitSurvey.header')} - onBackButtonPress={() => Navigation.goBack()} + onBackButtonPress={() => Navigation.goBack(exitReason ? ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON) : undefined)} /> {isOffline && <ExitSurveyOffline />} {!isOffline && ( @@ -59,6 +63,10 @@ function ExitSurveyConfirmPage({isLoading}: ExitSurveyConfirmPageOnyxProps) { ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; export default withOnyx<ExitSurveyConfirmPageOnyxProps, ExitSurveyConfirmPageOnyxProps>({ + exitReason: { + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, + selector: (value: OnyxEntry<OnyxTypes.ExitSurveyReasonForm>) => value?.[CONST.EXIT_SURVEY.REASON_INPUT_ID], + }, isLoading: { key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT, }, diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index e9ed2d3d16c9..ff297861b3b2 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -85,7 +85,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton title={translate('exitSurvey.header')} - onBackButtonPress={() => Navigation.goBack()} + onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_EXIT_SURVEY_REASON)} /> <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} From 50d47e9b5823dcc0e0f56756d8a55aa8bd0016ab Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 14:39:49 -0800 Subject: [PATCH 60/81] Move switch to Expensify Classic to top of account settings page --- src/pages/settings/InitialSettingsPage.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.js index 7acbd8fedfad..81666ed8fa78 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.js @@ -135,6 +135,11 @@ function InitialSettingsPage(props) { sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: [ + { + translationKey: 'exitSurvey.goToExpensifyClassic', + icon: Expensicons.NewExpensify, + routeName: ROUTES.SETTINGS_EXIT_SURVEY_REASON, + }, { translationKey: 'common.profile', icon: Expensicons.Profile, @@ -187,11 +192,6 @@ function InitialSettingsPage(props) { }, sectionTranslationKey: 'initialSettingsPage.general', items: [ - { - translationKey: 'exitSurvey.goToExpensifyClassic', - icon: Expensicons.NewExpensify, - routeName: ROUTES.SETTINGS_EXIT_SURVEY_REASON, - }, { translationKey: 'initialSettingsPage.help', icon: Expensicons.QuestionMark, From 3ee883283fc0aef1c7f74c3ff014cf436b068ea4 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Fri, 2 Feb 2024 18:35:30 -0800 Subject: [PATCH 61/81] Subtract keyboard height from maxHeight of form and input --- src/components/withKeyboardState.tsx | 20 +++++++++++++------ .../ExitSurvey/ExitSurveyResponsePage.tsx | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/withKeyboardState.tsx b/src/components/withKeyboardState.tsx index 74d10945fbcb..560576fdbf5c 100755 --- a/src/components/withKeyboardState.tsx +++ b/src/components/withKeyboardState.tsx @@ -8,27 +8,34 @@ import type ChildrenProps from '@src/types/utils/ChildrenProps'; type KeyboardStateContextValue = { /** Whether the keyboard is open */ isKeyboardShown: boolean; + + /** Height of the keyboard in pixels */ + keyboardHeight: number; }; // TODO: Remove - left for backwards compatibility with existing components (https://github.com/Expensify/App/issues/25151) const keyboardStatePropTypes = { /** Whether the keyboard is open */ isKeyboardShown: PropTypes.bool.isRequired, + + /** Height of the keyboard in pixels */ + keyboardHeight: PropTypes.number.isRequired, }; const KeyboardStateContext = createContext<KeyboardStateContextValue>({ isKeyboardShown: false, + keyboardHeight: 0, }); function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null { - const [isKeyboardShown, setIsKeyboardShown] = useState(false); + const [keyboardHeight, setKeyboardHeight] = useState(0); useEffect(() => { - const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => { - setIsKeyboardShown(true); + const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (e) => { + setKeyboardHeight(e.endCoordinates.height); }); const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => { - setIsKeyboardShown(false); + setKeyboardHeight(0); }); return () => { @@ -39,9 +46,10 @@ function KeyboardStateProvider({children}: ChildrenProps): ReactElement | null { const contextValue = useMemo( () => ({ - isKeyboardShown, + keyboardHeight, + isKeyboardShown: keyboardHeight !== 0, }), - [isKeyboardShown], + [keyboardHeight], ); return <KeyboardStateContext.Provider value={contextValue}>{children}</KeyboardStateContext.Provider>; } diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index ff297861b3b2..be9efe3adf2b 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -10,6 +10,7 @@ import ScreenWrapper from '@components/ScreenWrapper'; import Text from '@components/Text'; import TextInput from '@components/TextInput'; import useKeyboardShortcut from '@hooks/useKeyboardShortcut'; +import useKeyboardState from '@hooks/useKeyboardState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; @@ -42,6 +43,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr const {isOffline} = useNetwork(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + const {keyboardHeight} = useKeyboardState(); const {windowHeight} = useWindowDimensions(); const {top: safeAreaInsetsTop} = useSafeAreaInsets(); @@ -58,6 +60,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr const baseResponseInputContainerStyle = styles.mt7; const formMaxHeight = Math.floor( windowHeight - + keyboardHeight - safeAreaInsetsTop - // Minus the height of HeaderWithBackButton variables.contentHeaderHeight - From dcd132f3cff066a3004413595f04ad1f36ac31d4 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 16:41:55 -0800 Subject: [PATCH 62/81] Make textinput uncontrolled for better performance on android, remove outdated comment --- src/components/TextInput/BaseTextInput/types.ts | 4 ---- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 1 - 2 files changed, 5 deletions(-) diff --git a/src/components/TextInput/BaseTextInput/types.ts b/src/components/TextInput/BaseTextInput/types.ts index a637dc22d72e..d852d83220f5 100644 --- a/src/components/TextInput/BaseTextInput/types.ts +++ b/src/components/TextInput/BaseTextInput/types.ts @@ -51,15 +51,11 @@ type CustomBaseTextInputProps = { /** * Autogrow input container length based on the entered text. - * Note: If you use this prop, the text input has to be controlled - * by a value prop. */ autoGrow?: boolean; /** * Autogrow input container height based on the entered text - * Note: If you use this prop, the text input has to be controlled - * by a value prop. */ autoGrowHeight?: boolean; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index be9efe3adf2b..0b4b949cb224 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -123,7 +123,6 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr } updateMultilineInputRange(el); }} - value={draftResponse} containerStyles={[baseResponseInputContainerStyle, StyleUtils.getMaximumHeight(responseInputMaxHeight)]} shouldSaveDraft /> From 57fdf9464c4de48e95e55c2c54801b4ca4b5164d Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 16:57:35 -0800 Subject: [PATCH 63/81] Fix offline icon on mobile --- src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index c7d8885c1944..80834a683f2c 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import Icon from '@components//Icon'; import Button from '@components/Button'; import FixedFooter from '@components/FixedFooter'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -12,6 +13,7 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import variables from '@styles/variables'; import * as ExitSurvey from '@userActions/ExitSurvey'; import type {ExitReason} from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; @@ -39,7 +41,11 @@ function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOny {isOffline && <ExitSurveyOffline />} {!isOffline && ( <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> - <MushroomTopHat /> + <Icon + src={MushroomTopHat} + width={variables.modalTopIconWidth} + height={variables.modalTopIconHeight} + /> <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> </View> From 13cd0e5ef6555cf53738fede80356b97c613a8c9 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 16:57:46 -0800 Subject: [PATCH 64/81] Add padding to offline view --- src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx index a02ee48d1943..2e89036f62b4 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx @@ -11,7 +11,7 @@ function ExitSurveyOffline() { const {translate} = useLocalize(); const styles = useThemeStyles(); return ( - <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter]}> + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> <Icon width={variables.modalTopIconWidth} height={variables.modalTopIconHeight} From f0091e4155bfc097f83bd214cb0fde0cd6594141 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 17:11:45 -0800 Subject: [PATCH 65/81] Fix offline and mushroomtophat styles --- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 26 ++++++++++--------- .../settings/ExitSurvey/ExitSurveyOffline.tsx | 2 +- src/styles/variables.ts | 3 +++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 80834a683f2c..a813a05bff5d 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -38,18 +38,20 @@ function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOny title={translate('exitSurvey.header')} onBackButtonPress={() => Navigation.goBack(exitReason ? ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON) : undefined)} /> - {isOffline && <ExitSurveyOffline />} - {!isOffline && ( - <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> - <Icon - src={MushroomTopHat} - width={variables.modalTopIconWidth} - height={variables.modalTopIconHeight} - /> - <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> - <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> - </View> - )} + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.mh5]}> + {isOffline && <ExitSurveyOffline />} + {!isOffline && ( + <> + <Icon + src={MushroomTopHat} + width={variables.mushroomTopHatWidth} + height={variables.mushroomTopHatHeight} + /> + <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> + <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> + </> + )} + </View> <FixedFooter> <Button success diff --git a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx index 2e89036f62b4..a02ee48d1943 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx @@ -11,7 +11,7 @@ function ExitSurveyOffline() { const {translate} = useLocalize(); const styles = useThemeStyles(); return ( - <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.p5]}> + <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter]}> <Icon width={variables.modalTopIconWidth} height={variables.modalTopIconHeight} diff --git a/src/styles/variables.ts b/src/styles/variables.ts index c6d2bebe1417..f450a185a978 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -214,4 +214,7 @@ export default { updateAnimationH: 240, updateTextViewContainerWidth: 310, updateViewHeaderHeight: 70, + + mushroomTopHatWidth: 138, + mushroomTopHatHeight: 128, } as const; From adbae00022083ca08e99e4d41532597a3cef892d Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 18:00:39 -0800 Subject: [PATCH 66/81] Fix lockfiles after merge --- ios/Podfile.lock | 2 +- package-lock.json | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1664c982ce50..5b66969747c1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1981,7 +1981,7 @@ SPEC CHECKSUMS: SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Turf: 13d1a92d969ca0311bbc26e8356cca178ce95da2 VisionCamera: fda554d8751e395effcc87749f8b7c198c1031be - Yoga: 13c8ef87792450193e117976337b8527b49e8c03 + Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 PODFILE CHECKSUM: 0ccbb4f2406893c6e9f266dc1e7470dcd72885d2 diff --git a/package-lock.json b/package-lock.json index eaf3aba67c85..1b3b96cf318c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,7 @@ "react-native-linear-gradient": "^2.8.1", "react-native-localize": "^2.2.6", "react-native-modal": "^13.0.0", - "react-native-onyx": "2.0.2", + "react-native-onyx": "2.0.4", "react-native-pager-view": "6.2.2", "react-native-pdf": "6.7.3", "react-native-performance": "^5.1.0", @@ -233,7 +233,7 @@ "style-loader": "^2.0.0", "time-analytics-webpack-plugin": "^0.1.17", "ts-node": "^10.9.2", - "type-fest": "^3.12.0", + "type-fest": "^4.10.2", "typescript": "^5.3.2", "wait-port": "^0.2.9", "webpack": "^5.76.0", @@ -8133,9 +8133,9 @@ } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", + "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", "dev": true, "dependencies": { "ansi-html-community": "^0.0.8", @@ -8155,7 +8155,7 @@ "@types/webpack": "4.x || 5.x", "react-refresh": ">=0.10.0 <1.0.0", "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", + "type-fest": ">=0.17.0 <5.0.0", "webpack": ">=4.43.0 <6.0.0", "webpack-dev-server": "3.x || 4.x", "webpack-hot-middleware": "2.x", @@ -26846,10 +26846,11 @@ } }, "node_modules/core-js-pure": { - "version": "3.24.1", + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", + "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -38585,6 +38586,17 @@ "node": ">=8" } }, + "node_modules/jest-watch-typeahead/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-watcher": { "version": "29.4.1", "license": "MIT", @@ -45112,9 +45124,9 @@ } }, "node_modules/react-native-onyx": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.2.tgz", - "integrity": "sha512-24kcG3ChBXp+uSSCXudFvZTdCnKLRHQRgvTcnh2eA7COtKvbL8ggEJNkglSYmcf5WoDzLgYyWiKvcjcXQnmBvw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-native-onyx/-/react-native-onyx-2.0.4.tgz", + "integrity": "sha512-QpeqqpvH4XGTWgPK4qJ6NoBvHDXFWtbLOuGBN7iqd3RVFx+juxgiuaf+Zr0GEpR2P40vsX5IA0m1SiAotvzbmA==", "dependencies": { "ascii-table": "0.0.9", "fast-equals": "^4.0.3", @@ -50784,11 +50796,12 @@ } }, "node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz", + "integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==", + "dev": true, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" From e3ac42d4b13785c38cd4aef41974d392806025a8 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Thu, 8 Feb 2024 23:03:07 -0800 Subject: [PATCH 67/81] Center align text on offline and confirm page --- src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx | 4 ++-- src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index a813a05bff5d..042cbe284bb5 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -47,8 +47,8 @@ function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOny width={variables.mushroomTopHatWidth} height={variables.mushroomTopHatHeight} /> - <Text style={[styles.headerAnonymousFooter, styles.mt5]}>{translate('exitSurvey.thankYou')}</Text> - <Text style={[styles.mt2]}>{translate('exitSurvey.thankYouSubtitle')}</Text> + <Text style={[styles.headerAnonymousFooter, styles.mt5, styles.textAlignCenter]}>{translate('exitSurvey.thankYou')}</Text> + <Text style={[styles.mt2, styles.textAlignCenter]}>{translate('exitSurvey.thankYouSubtitle')}</Text> </> )} </View> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx index a02ee48d1943..3363867ad4bb 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyOffline.tsx @@ -17,8 +17,8 @@ function ExitSurveyOffline() { height={variables.modalTopIconHeight} src={ToddBehindCloud} /> - <Text style={styles.headerAnonymousFooter}>{translate('exitSurvey.offlineTitle')}</Text> - <Text style={styles.mt2}>{translate('exitSurvey.offline')}</Text> + <Text style={[styles.headerAnonymousFooter, styles.textAlignCenter]}>{translate('exitSurvey.offlineTitle')}</Text> + <Text style={[styles.mt2, styles.textAlignCenter]}>{translate('exitSurvey.offline')}</Text> </View> ); } From 90a0b4a8c0a9108beef9b11c3969a01917187081 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 09:48:04 -0800 Subject: [PATCH 68/81] Fix most types after merge --- src/CONST.ts | 2 -- src/ONYXKEYS.ts | 2 ++ src/components/Form/FormProvider.tsx | 4 ++-- src/libs/FormUtils.ts | 2 +- src/libs/Navigation/types.ts | 3 ++- src/libs/actions/ExitSurvey.ts | 16 +++++++--------- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 6 +++--- .../settings/ExitSurvey/ExitSurveyReasonPage.tsx | 7 ++++--- .../ExitSurvey/ExitSurveyResponsePage.tsx | 14 +++++++------- src/types/form/ExitSurveyReasonForm.ts | 16 ++++++++++++++++ src/types/form/ExitSurveyResponseForm.ts | 12 ++++++++++++ src/types/form/index.ts | 2 ++ 12 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 src/types/form/ExitSurveyReasonForm.ts create mode 100644 src/types/form/ExitSurveyResponseForm.ts diff --git a/src/CONST.ts b/src/CONST.ts index 02f720c638bd..c83da086b281 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -3256,8 +3256,6 @@ const CONST = { DONT_UNDERSTAND: 'dontUnderstand', PREFER_CLASSIC: 'preferClassic', }, - REASON_INPUT_ID: 'reason', - RESPONSE_INPUT_ID: 'response', }, } as const; diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 07ecb7d50ca8..10232f04e843 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -404,6 +404,8 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.ROOM_SETTINGS_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.NEW_TASK_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.EDIT_TASK_FORM]: FormTypes.Form; + [ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM]: FormTypes.ExitSurveyReasonForm; + [ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM]: FormTypes.ExitSurveyResponseForm; [ONYXKEYS.FORMS.MONEY_REQUEST_DESCRIPTION_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_MERCHANT_FORM]: FormTypes.Form; [ONYXKEYS.FORMS.MONEY_REQUEST_AMOUNT_FORM]: FormTypes.Form; diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index ff808c8ee45e..ad09b68a5f39 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -1,5 +1,5 @@ import lodashIsEqual from 'lodash/isEqual'; -import type {FocusEvent, ForwardedRef, MutableRefObject, ReactNode} from 'react'; +import type {ForwardedRef, MutableRefObject, ReactNode} from 'react'; import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputSubmitEditingEventData, ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -311,7 +311,7 @@ function FormProvider( } inputProps.onPressOut?.(event); }, - onBlur: (event: FocusEvent) => { + onBlur: (event) => { // Only run validation when user proactively blurs the input. if (Visibility.isVisible() && Visibility.hasFocus()) { const relatedTarget = event && 'relatedTarget' in event.nativeEvent && event?.nativeEvent?.relatedTarget; diff --git a/src/libs/FormUtils.ts b/src/libs/FormUtils.ts index 4d0571ada6f2..1f854c311454 100644 --- a/src/libs/FormUtils.ts +++ b/src/libs/FormUtils.ts @@ -1,7 +1,7 @@ import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS'; function getDraftKey(formID: OnyxFormKey): OnyxFormDraftKey { - return `${formID}Draft`; + return `${formID}Draft` as const; } export default {getDraftKey}; diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 4957d8cd06e4..d6f5eaf72605 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -15,6 +15,7 @@ import type CONST from '@src/CONST'; import type NAVIGATORS from '@src/NAVIGATORS'; import type {HybridAppRoute, Route as Routes} from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; +import type EXIT_SURVEY_REASON_FORM_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; type NavigationRef = NavigationContainerRefWithCurrent<RootStackParamList>; @@ -163,7 +164,7 @@ type SettingsNavigatorParamList = { [SCREENS.KEYBOARD_SHORTCUTS]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { - [CONST.EXIT_SURVEY.REASON_INPUT_ID]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + [EXIT_SURVEY_REASON_FORM_INPUT_IDS.REASON]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; }; [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: undefined; } & ReimbursementAccountNavigatorParamList; diff --git a/src/libs/actions/ExitSurvey.ts b/src/libs/actions/ExitSurvey.ts index 7b3ba839397c..ef3ecd6d3e31 100644 --- a/src/libs/actions/ExitSurvey.ts +++ b/src/libs/actions/ExitSurvey.ts @@ -1,29 +1,28 @@ import type {OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; import * as API from '@libs/API'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; - -type ExitReason = ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; +import REASON_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; +import type {ExitReason} from '@src/types/form/ExitSurveyReasonForm'; +import RESPONSE_INPUT_IDS from '@src/types/form/ExitSurveyResponseForm'; let exitReason: ExitReason | undefined; let exitSurveyResponse: string | undefined; Onyx.connect({ key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, - callback: (value) => (exitReason = value?.[CONST.EXIT_SURVEY.REASON_INPUT_ID]), + callback: (value) => (exitReason = value?.[REASON_INPUT_IDS.REASON]), }); Onyx.connect({ key: ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, - callback: (value) => (exitSurveyResponse = value?.[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]), + callback: (value) => (exitSurveyResponse = value?.[RESPONSE_INPUT_IDS.RESPONSE]), }); function saveExitReason(reason: ExitReason) { - Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[CONST.EXIT_SURVEY.REASON_INPUT_ID]: reason}); + Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, {[REASON_INPUT_IDS.REASON]: reason}); } function saveResponse(response: string) { - Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID]: response}); + Onyx.set(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM, {[RESPONSE_INPUT_IDS.RESPONSE]: response}); } /** @@ -77,4 +76,3 @@ function switchToOldDot() { } export {saveExitReason, saveResponse, switchToOldDot}; -export type {ExitReason}; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 042cbe284bb5..15f9becd6580 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -15,12 +15,12 @@ import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import variables from '@styles/variables'; import * as ExitSurvey from '@userActions/ExitSurvey'; -import type {ExitReason} from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; +import type {ExitReason, ExitSurveyReasonForm} from '@src/types/form/ExitSurveyReasonForm'; +import EXIT_SURVEY_REASON_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyConfirmPageOnyxProps = { @@ -73,7 +73,7 @@ ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; export default withOnyx<ExitSurveyConfirmPageOnyxProps, ExitSurveyConfirmPageOnyxProps>({ exitReason: { key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, - selector: (value: OnyxEntry<OnyxTypes.ExitSurveyReasonForm>) => value?.[CONST.EXIT_SURVEY.REASON_INPUT_ID], + selector: (value: OnyxEntry<ExitSurveyReasonForm>) => value?.[EXIT_SURVEY_REASON_INPUT_IDS.REASON], }, isLoading: { key: ONYXKEYS.IS_SWITCHING_TO_OLD_DOT, diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index acd55210af6f..b62deb323ac7 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -11,10 +11,11 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; import * as ExitSurvey from '@userActions/ExitSurvey'; -import type {ExitReason} from '@userActions/ExitSurvey'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {ExitReason} from '@src/types/form/ExitSurveyReasonForm'; +import INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import ExitSurveyOffline from './ExitSurveyOffline'; @@ -46,7 +47,7 @@ function ExitSurveyReasonPage() { validate={() => { const errors: Errors = {}; if (!reason) { - errors[CONST.EXIT_SURVEY.REASON_INPUT_ID] = 'common.error.fieldRequired'; + errors[INPUT_IDS.REASON] = 'common.error.fieldRequired'; } return errors; }} @@ -68,7 +69,7 @@ function ExitSurveyReasonPage() { <Text style={styles.mt2}>{translate('exitSurvey.reasonPage.subtitle')}</Text> <InputWrapper InputComponent={RadioButtons} - inputID={CONST.EXIT_SURVEY.REASON_INPUT_ID} + inputID={INPUT_IDS.REASON} items={reasons} onPress={(value) => setReason(value as ExitReason)} /> diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 0b4b949cb224..b09a3ea49f38 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,9 +1,9 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; +import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -17,7 +17,6 @@ import useSafeAreaInsets from '@hooks/useSafeAreaInsets'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import FormUtils from '@libs/FormUtils'; import * as NumberUtils from '@libs/NumberUtils'; import updateMultilineInputRange from '@libs/updateMultilineInputRange'; import Navigation from '@navigation/Navigation'; @@ -28,7 +27,8 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; +import type {ExitSurveyResponseForm} from '@src/types/form'; +import INPUT_IDS from '@src/types/form/ExitSurveyResponseForm'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import ExitSurveyOffline from './ExitSurveyOffline'; @@ -98,7 +98,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr validate={() => { const errors: Errors = {}; if (!draftResponse?.trim()) { - errors[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID] = 'common.error.fieldRequired'; + errors[INPUT_IDS.RESPONSE] = 'common.error.fieldRequired'; } return errors; }} @@ -111,7 +111,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr <Text style={textStyle}>{translate(`exitSurvey.prompts.${reason}`)}</Text> <InputWrapper InputComponent={TextInput} - inputID={CONST.EXIT_SURVEY.RESPONSE_INPUT_ID} + inputID={INPUT_IDS.RESPONSE} label={translate(`exitSurvey.responsePlaceholder`)} accessibilityLabel={translate(`exitSurvey.responsePlaceholder`)} role={CONST.ROLE.PRESENTATION} @@ -137,7 +137,7 @@ ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; export default withOnyx<ExitSurveyResponsePageProps, ExitSurveyResponsePageOnyxProps>({ draftResponse: { - key: FormUtils.getDraftKey(ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM), - selector: (value: OnyxEntry<OnyxTypes.ExitSurveyResponseForm>) => value?.[CONST.EXIT_SURVEY.RESPONSE_INPUT_ID] ?? '', + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, + selector: (value: FormOnyxValues<typeof ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM>) => value?.[INPUT_IDS.RESPONSE] ?? '', }, })(ExitSurveyResponsePage); diff --git a/src/types/form/ExitSurveyReasonForm.ts b/src/types/form/ExitSurveyReasonForm.ts new file mode 100644 index 000000000000..48f10d4d163d --- /dev/null +++ b/src/types/form/ExitSurveyReasonForm.ts @@ -0,0 +1,16 @@ +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; +import type Form from './Form'; + +type ExitReason = ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + +const INPUT_IDS = { + REASON: 'reason', +} as const; + +type ExitSurveyReasonForm = Form<{ + [INPUT_IDS.REASON]: ExitReason; +}>; + +export type {ExitSurveyReasonForm, ExitReason}; +export default INPUT_IDS; diff --git a/src/types/form/ExitSurveyResponseForm.ts b/src/types/form/ExitSurveyResponseForm.ts new file mode 100644 index 000000000000..6776b29b8054 --- /dev/null +++ b/src/types/form/ExitSurveyResponseForm.ts @@ -0,0 +1,12 @@ +import type Form from './Form'; + +const INPUT_IDS = { + RESPONSE: 'response', +} as const; + +type ExitSurveyResponseForm = Form<{ + [INPUT_IDS.RESPONSE]: string; +}>; + +export type {ExitSurveyResponseForm}; +export default INPUT_IDS; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index eb691bcc8ca4..17ffa17e9dfc 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -3,6 +3,8 @@ export type {CloseAccountForm} from './CloseAccountForm'; export type {DateOfBirthForm} from './DateOfBirthForm'; export type {DisplayNameForm} from './DisplayNameForm'; export type {EditTaskForm} from './EditTaskForm'; +export type {ExitSurveyReasonForm} from './ExitSurveyReasonForm'; +export type {ExitSurveyResponseForm} from './ExitSurveyResponseForm'; export type {GetPhysicalCardForm} from './GetPhysicalCardForm'; export type {HomeAddressForm} from './HomeAddressForm'; export type {IKnowTeacherForm} from './IKnowTeacherForm'; From 349f6d83b9ded15ba4f19b016e76626f78ad46d2 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 10:09:58 -0800 Subject: [PATCH 69/81] Fix types in ExitSurveyResponsePage --- src/libs/FormUtils.ts | 2 +- src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libs/FormUtils.ts b/src/libs/FormUtils.ts index 1f854c311454..4d0571ada6f2 100644 --- a/src/libs/FormUtils.ts +++ b/src/libs/FormUtils.ts @@ -1,7 +1,7 @@ import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS'; function getDraftKey(formID: OnyxFormKey): OnyxFormDraftKey { - return `${formID}Draft` as const; + return `${formID}Draft`; } export default {getDraftKey}; diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index b09a3ea49f38..9a23e3406521 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -3,7 +3,6 @@ import React, {useCallback} from 'react'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import type {FormOnyxValues} from '@components/Form/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import type {AnimatedTextInputRef} from '@components/RNTextInput'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -27,7 +26,6 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {ExitSurveyResponseForm} from '@src/types/form'; import INPUT_IDS from '@src/types/form/ExitSurveyResponseForm'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import ExitSurveyOffline from './ExitSurveyOffline'; @@ -137,7 +135,7 @@ ExitSurveyResponsePage.displayName = 'ExitSurveyResponsePage'; export default withOnyx<ExitSurveyResponsePageProps, ExitSurveyResponsePageOnyxProps>({ draftResponse: { - key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, - selector: (value: FormOnyxValues<typeof ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM>) => value?.[INPUT_IDS.RESPONSE] ?? '', + key: ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM_DRAFT, + selector: (value) => value?.[INPUT_IDS.RESPONSE] ?? '', }, })(ExitSurveyResponsePage); From 45882a4e98d0a543c2eadcfcd9220c554c98cefc Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 10:27:07 -0800 Subject: [PATCH 70/81] Fix typo in english translation --- src/languages/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index eeefe60005b2..aaee8997c4fb 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -2333,7 +2333,7 @@ export default { prompts: { [CONST.EXIT_SURVEY.REASONS.FEATURE_NOT_AVAILABLE]: "What feature do you need that isn't available in New Expensify?", [CONST.EXIT_SURVEY.REASONS.DONT_UNDERSTAND]: 'What are you trying to do?', - [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'Why to you prefer Expensify Classic?', + [CONST.EXIT_SURVEY.REASONS.PREFER_CLASSIC]: 'Why do you prefer Expensify Classic?', }, responsePlaceholder: 'Your response', thankYou: 'Thanks for the feedback!', From 07a802d9c5d5bc27121208c7f5a9c0525516a49e Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 10:28:48 -0800 Subject: [PATCH 71/81] Update MushroomTopHat illustration --- .../mushroom-top-hat.svg | 213 ++++++++++++------ 1 file changed, 141 insertions(+), 72 deletions(-) diff --git a/assets/images/product-illustrations/mushroom-top-hat.svg b/assets/images/product-illustrations/mushroom-top-hat.svg index f09f7e7378ed..cb808f7289e0 100644 --- a/assets/images/product-illustrations/mushroom-top-hat.svg +++ b/assets/images/product-illustrations/mushroom-top-hat.svg @@ -1,73 +1,142 @@ -<svg width="136" height="128" viewBox="0 0 136 128" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g id="Illustration"> -<path id="Vector" d="M20.9596 119.21C20.9596 119.21 21.6229 120.98 23.2829 120.98C24.9429 120.98 27.3762 121.09 27.3762 121.09L28.9262 122.416C28.9262 122.416 30.1429 126.62 33.4595 126.176C36.7761 125.733 36.5561 123.19 36.5561 123.19C36.5561 123.19 39.4328 122.526 39.8728 120.203C39.8728 120.203 42.5261 119.76 42.7494 117.66C42.7494 117.66 47.2894 119.186 53.2026 116.266C59.1159 113.346 58.0126 107.043 58.0126 107.043C58.0126 107.043 55.5793 100.74 54.8059 100.187C54.0326 99.6332 51.8193 97.7533 51.8193 97.7533L21.9563 89.0167L31.1362 109.92C31.1362 109.92 17.753 110.803 20.9596 119.21Z" fill="#FFFAF0"/> -<path id="Vector_2" d="M4.70031 76.5168C4.70031 76.5168 4.36697 83.3734 15.7602 87.0234C27.1534 90.6733 49.9365 97.1999 49.9365 97.1999C49.9365 97.1999 66.6364 100.85 69.0697 92.0033C71.503 83.1567 67.1297 80.8901 63.873 78.8434C63.873 78.8434 49.1632 70.7702 33.9 67.2303C18.6368 63.6903 10.7836 63.2503 6.58362 70.1069C5.70029 72.5402 4.59364 75.5267 4.70364 76.5201L4.70031 76.5168Z" fill="#03D47C"/> -<path id="Vector_3" d="M27.2634 85.3667C30.868 85.3667 33.79 82.4448 33.79 78.8402C33.79 75.2357 30.868 72.3136 27.2634 72.3136C23.6589 72.3136 20.7368 75.2357 20.7368 78.8402C20.7368 82.4448 23.6589 85.3667 27.2634 85.3667Z" fill="#FFFAF0"/> -<path id="Vector_4" d="M40.5361 89.7899C44.1407 89.7899 47.0627 86.8678 47.0627 83.2633C47.0627 79.6587 44.1407 76.7368 40.5361 76.7368C36.9316 76.7368 34.0095 79.6587 34.0095 83.2633C34.0095 86.8678 36.9316 89.7899 40.5361 89.7899Z" fill="#FFFAF0"/> -<path id="Vector_5" d="M35.3361 52.187C35.3361 52.187 29.5129 46.5837 24.9396 53.2203C20.3696 59.8569 28.0362 65.3136 28.0362 65.3136L39.6861 69.0003L47.5027 68.4103C47.5027 68.4103 43.0794 65.3136 46.176 62.3636C49.2727 59.4137 51.336 65.167 55.1726 63.397C59.0059 61.627 58.5659 54.4004 54.2893 51.6004L52.666 52.7804L51.7826 7.36076C51.7826 7.36076 50.3093 0.577492 42.9361 1.02082C35.5628 1.46415 35.1195 5.44418 34.6795 9.42747C34.2362 13.4074 35.3428 52.1904 35.3428 52.1904L35.3361 52.187Z" fill="#F68DFE"/> -<path id="Vector_6" d="M35.3363 52.1869C35.3363 52.1869 35.363 52.207 35.4097 52.247C36.0463 52.7703 40.6796 56.477 44.0729 56.167C47.7229 55.8337 52.1462 52.737 52.1462 52.737L52.3662 47.7604C52.3662 47.7604 44.5129 45.217 35.333 47.6503V52.1837L35.3363 52.1869Z" fill="#E4BC07"/> -<path id="Vector_7" d="M79.5758 112.793L75.9259 123.19C75.9259 123.19 75.5925 125.07 78.8025 125.07H97.6057C97.6057 125.07 100.369 123.853 100.482 122.083C100.592 120.313 99.819 119.206 99.819 119.206L87.4324 113.233L79.5792 112.79L79.5758 112.793Z" fill="#FED607"/> -<path id="Vector_8" d="M121.492 111.356V119.1H132.662C132.662 119.1 134.762 117.883 134.762 115.12V98.64C134.762 98.64 133.879 95.3233 131.666 95.1C129.452 94.8766 128.236 96.87 128.236 96.87L121.489 111.36L121.492 111.356Z" fill="#FED607"/> -<path id="Vector_9" d="M23.54 94.5834L21.0334 89.1267L1.42023 111.1C1.42023 111.1 0.903572 114.86 1.64023 116.41C2.37689 117.96 5.77018 119.36 5.77018 119.36L20.5167 119.213C20.5167 119.213 19.1167 114.566 21.6234 112.873L8.86682 112.726L8.05683 112.283L23.54 94.5866V94.5834Z" fill="#002140"/> -<path id="Vector_10" d="M46.103 118.62H77.5127L79.4294 112.13L57.3096 112.276C57.3096 112.276 51.263 117.436 49.3463 117.66C47.4297 117.883 46.103 118.62 46.103 118.62Z" fill="#002140"/> -<path id="Vector_11" d="M87.4287 113.016L99.7053 119.543H120.938C120.938 119.543 121.712 113.35 120.828 111.8H87.5387L87.4287 113.016Z" fill="#002140"/> -<path id="Vector_12" d="M79.4293 112.13C79.4293 112.13 84.8859 114.01 87.4292 113.013L98.1558 84.3667C98.1558 84.3667 99.8157 78.1734 94.5058 76.8468C89.1958 75.5201 83.0026 83.0401 83.0026 83.0401L58.0061 109.473L57.3062 112.276L67.2961 112.24L85.8759 90.8933C85.8759 90.8933 87.0925 90.34 86.7592 92.11C86.4259 93.8799 79.4226 112.13 79.4226 112.13H79.4293Z" fill="#002140"/> -<path id="Vector_13" d="M76.3691 88.6834L75.7057 91.3367L80.7924 86.9134L82.3423 82.8201C82.3423 82.8201 80.4624 67.7802 62.5459 65.5669C62.5459 65.5669 60.7759 66.4503 61.2192 69.1069C61.2192 69.1069 76.5157 71.8302 76.3724 88.6834H76.3691Z" fill="#002140"/> -<path id="Vector_14" d="M51.4826 63.1369C51.4826 63.1369 50.4859 61.4769 48.276 61.5869C46.0626 61.6969 45.2893 63.1368 45.2893 64.0201C45.2893 64.9035 46.616 68.3335 51.3726 69.3302C56.1292 70.3268 60.8825 69.5502 60.8825 69.5502C60.8825 69.5502 60.3292 66.0102 64.4225 64.6835C68.5157 63.3569 73.3057 61.3303 73.3824 60.037C73.4557 58.747 72.499 57.1603 71.3924 57.3837C71.3924 57.3837 72.609 54.8403 70.7291 54.067C68.8491 53.2936 64.9791 54.5104 62.8758 57.827C62.8758 57.827 62.1025 50.417 57.6792 48.867C54.9159 47.6503 54.3625 51.4104 54.3625 51.4104C54.3625 51.4104 61.1092 57.9369 56.4625 62.5802C53.7259 64.6269 51.4859 63.1336 51.4859 63.1336L51.4826 63.1369Z" fill="#FFFAF0"/> -<path id="Vector_15" d="M41.3093 105.383C41.3093 105.383 40.976 106.71 39.4294 106.046C37.8794 105.383 37.9927 103.503 37.9927 103.503C37.9927 103.503 35.1894 105.053 34.8961 103.283C34.6027 101.513 34.0127 101.733 35.2294 100.52C36.446 99.3064 37.8827 96.9799 37.8827 96.9799C37.8827 96.9799 35.1194 94.9432 33.6794 95.3199C32.2394 95.6966 30.1394 97.6432 30.1394 98.3065C30.1394 98.9699 29.9195 105.606 38.4327 108.04C46.9493 110.473 48.0559 107.706 48.6093 106.38C49.1626 105.053 50.2692 103.726 48.1659 102.73C46.0659 101.733 44.296 101.18 44.296 101.18L41.3093 105.383Z" fill="#002140"/> -<path id="Vector_16" d="M36.4893 53.0404L38.2592 54.5871L45.2625 46.9939L41.4292 47.5839L36.4893 53.0404Z" fill="#002140"/> -<path id="Vector_17" d="M41.1362 55.8405L44.5995 55.9904L52.2695 48.0271L49.0995 47.6571L41.1362 55.8405Z" fill="#002140"/> -<path id="Vector_18" d="M18.5 64.5402C18.5 64.5402 12.3801 65.2769 10.2434 66.6035C10.2434 66.6035 10.3901 74.1236 16.4367 72.2802C22.4833 70.4369 19.3866 64.6137 18.5 64.537V64.5402Z" fill="#002140"/> -<path id="Vector_19" d="M12.0835 75.3801C12.0835 75.3801 7.14358 75.3802 6.18359 80.1001C6.18359 80.1001 9.06022 84.0801 11.1235 84.8934C11.1235 84.8934 15.1802 84.8201 15.6935 81.2801C16.2102 77.7402 13.7035 75.4568 12.0802 75.3801H12.0835Z" fill="#002140"/> -<path id="Vector_20" d="M41.5028 69.7037C41.5028 69.7037 33.8362 66.9003 32.5062 66.977C31.1796 67.0503 31.3996 72.6536 35.7495 73.6869C40.0995 74.7202 41.4995 69.707 41.4995 69.707L41.5028 69.7037Z" fill="#002140"/> -<path id="Vector_21" d="M60.8225 77.6669L53.8925 74.3502C53.8925 74.3502 51.6792 76.3035 51.4592 77.5201C51.2392 78.7368 51.8292 83.0501 55.0725 83.4201C58.3158 83.7901 60.9725 81.0601 60.8225 77.6702V77.6669Z" fill="#002140"/> -<path id="Vector_22" d="M67.529 82.4601C67.529 82.4601 64.8024 83.1967 64.9491 86.3666C65.0957 89.5366 69.4457 90.1999 69.4457 90.1999C69.4457 90.1999 71.659 84.2266 67.529 82.4567V82.4601Z" fill="#002140"/> -<path id="Vector_23" d="M54.5525 97.9432L59.4924 98.3865C59.4924 98.3865 62.6624 95.8798 61.9991 93.5199C61.3358 91.1599 59.4191 89.2433 56.3958 89.4633C53.3725 89.6833 52.7592 92.6899 52.6358 93.4432C52.0458 95.4332 54.0358 97.7932 54.5525 97.9399V97.9432Z" fill="#002140"/> -<path id="Vector_24" d="M34.646 101.703L38.1126 97.1333C38.1126 97.1333 33.7627 93.6667 31.1094 96.9867C28.4561 100.303 32.806 104.95 32.806 104.95C32.806 104.95 38.706 109.3 43.1293 109.447C47.5526 109.593 48.9525 106.13 48.9525 106.13L48.7325 103.55L44.4559 101.043L40.476 106.28C40.476 106.28 39.3693 106.207 38.706 105.987C38.0426 105.767 38.2626 103.333 38.2626 103.333C38.2626 103.333 35.7193 104.253 35.2227 103.757C34.726 103.26 34.6527 101.71 34.6527 101.71L34.646 101.703Z" fill="#002140"/> -<path id="Vector_25" d="M57.946 98.6066C57.946 98.6066 60.4527 104.063 62.886 104.356L66.7926 100.743L63.476 97.2032L57.946 98.6031V98.6066Z" fill="#002140"/> -<path id="Vector_26" d="M30.2966 56.5803C30.2966 56.5803 42.6832 66.9769 53.8531 57.7969" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_27" d="M42.9031 70.0703C32.9332 67.1137 11.3667 58.217 5.58345 72.2202C1.86348 81.2234 11.0534 85.6201 18.1367 87.8301C27.7799 90.84 37.4198 93.88 47.1497 96.6C54.6496 98.6933 64.3429 100.733 68.7628 92.3933C69.5195 90.9634 70.0328 89.38 70.0428 87.7633C70.0728 82.0801 63.4162 78.9168 59.1296 76.7202C53.9563 74.0702 48.483 71.7236 42.9031 70.0703Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_28" d="M26.9794 85.7766C30.7662 85.7766 33.836 82.7562 33.836 79.0301C33.836 75.3041 30.7662 72.2834 26.9794 72.2834C23.1926 72.2834 20.1228 75.3041 20.1228 79.0301C20.1228 82.7562 23.1926 85.7766 26.9794 85.7766Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_29" d="M24.2501 78.1835L28.3867 78.4434C28.51 76.0001 27.0634 73.8634 24.9501 73.5268C22.6568 73.1635 20.4535 75.0535 20.0235 77.7468C19.5935 80.4401 21.1068 82.92 23.4001 83.2834C24.7367 83.4967 26.0401 82.9402 26.9801 81.9102L24.2468 78.1835H24.2501Z" fill="#002140"/> -<path id="Vector_30" d="M40.3959 90.0533C44.1827 90.0533 47.2525 87.0328 47.2525 83.3067C47.2525 79.5807 44.1827 76.5601 40.3959 76.5601C36.6091 76.5601 33.5393 79.5807 33.5393 83.3067C33.5393 87.0328 36.6091 90.0533 40.3959 90.0533Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_31" d="M37.6696 82.4601L41.8062 82.72C41.9296 80.2767 40.4829 78.1401 38.3696 77.8034C36.0763 77.4401 33.873 79.3301 33.443 82.0234C33.0163 84.7167 34.5263 87.1966 36.8196 87.56C38.1563 87.7733 39.4596 87.2166 40.3996 86.1866L37.6663 82.4601H37.6696Z" fill="#002140"/> -<path id="Vector_32" d="M47.733 68.3403C47.733 68.3403 41.9631 68.9135 39.5198 69.0135" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_33" d="M35.383 52.3771C35.383 52.3771 34.533 11.3774 34.533 10.0274C34.533 8.67739 33.9464 1.39422 42.9063 1.28088C50.4262 1.28088 51.8662 5.37422 51.8662 8.91085C51.7562 10.3475 52.4128 52.817 52.4128 52.817" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_34" d="M35.4932 47.5104C35.4932 47.5104 46.2197 46.2938 51.973 47.6204" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_35" d="M36.3428 53.187L41.946 47.0671" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_36" d="M38.4795 54.5136L45.4894 47.0038" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_37" d="M41.5029 55.547L49.2462 47.2039" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_38" d="M44.4526 56.0638L52.1959 47.7205" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_39" d="M63.9929 60.7804C67.9729 58.5671 72.6928 56.4303 73.5028 58.937C74.3128 61.4436 68.3429 63.7304 64.1396 65.057C59.9363 66.3837 61.3363 69.4803 61.3363 69.4803C61.3363 69.4803 57.3563 70.5136 53.6897 70.0803C51.043 69.7703 47.5697 68.7136 45.8464 66.5237C44.0064 64.1837 46.6764 60.4804 49.4764 61.6671C50.143 61.9504 50.6664 62.4837 51.2564 62.9037C52.7797 63.9837 55.2263 64.3937 56.623 62.9037C59.3463 60.0038 57.7963 53.3837 54.4097 51.597" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_40" d="M71.1423 57.7571C71.1423 57.7571 72.6923 55.6204 71.069 54.2904C69.4457 52.9637 64.6557 54.1437 62.5891 58.7137C63.0524 57.6903 62.3958 55.3437 62.1624 54.3371C61.5224 51.5638 59.6658 48.6171 56.4558 48.7071C54.8192 48.7504 54.1825 50.1305 54.4058 51.6005C52.7459 52.1538 51.4192 55.1404 44.6726 56.0237C37.926 56.9071 35.936 51.7105 31.7327 50.4939C27.5295 49.2772 24.6561 52.4838 23.8628 54.9038C22.8695 57.3104 22.8962 62.4737 28.8961 65.9136" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_41" d="M30.6297 109.776L21.1165 88.7633" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_42" d="M42.6826 117.85C42.6826 117.85 43.8993 118.403 46.5526 118.293C49.2059 118.183 58.0558 116.193 58.0558 108.783C58.0558 101.373 51.5292 97.7233 51.5292 97.7233" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_43" d="M30.2962 100.596C30.2962 100.596 30.7395 104.577 36.7094 107.453C39.5161 108.803 44.686 110.643 47.4793 108.263C48.9693 106.993 49.876 104.123 47.9927 102.81C46.3893 101.693 44.5094 101.066 42.7461 100.24C41.3661 99.5932 40.0261 98.8232 38.9261 97.7532C37.2528 96.1232 34.6095 94.7233 32.2862 95.8899C30.5562 96.7599 30.1028 98.8232 30.2995 100.6L30.2962 100.596Z" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_44" d="M37.8728 97.2233C37.8728 97.2233 36.4895 99.2698 35.1628 100.93C33.8362 102.59 34.7762 103.42 35.2195 103.75C35.6628 104.083 36.9161 104.45 38.2595 102.55C39.6061 100.65 40.5828 99.2698 40.5828 99.2698" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_45" d="M41.3393 99.7399C40.9226 100.347 39.8493 101.883 38.8093 103.18C37.4827 104.84 38.4226 105.67 38.866 106C39.3093 106.333 40.5626 106.7 41.9059 104.8C43.2526 102.9 44.2293 101.52 44.2293 101.52" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_46" d="M36.4897 123.38C36.4897 123.38 38.3697 123.546 39.3664 122.22C40.363 120.893 39.8097 117.353 35.1631 115.03" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_47" d="M40.016 120.486C40.656 120.416 41.6193 120.17 42.2426 119.343C43.2393 118.016 42.6859 114.476 38.0393 112.153C38.0393 112.153 33.8927 110.163 31.1261 110.163C24.3795 110.163 19.9562 113.04 20.0095 116.8C20.0095 116.8 20.1762 121.28 23.6595 121.17C27.1428 121.06 28.0828 120.95 28.9128 122.22C29.7428 123.493 30.9027 126.533 33.1694 126.533C35.436 126.533 36.266 125.373 36.266 123.656C36.266 121.94 33.4461 118.9 30.9027 117.573" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_48" d="M21.4496 113.04H9.143C8.63634 113.04 8.36301 112.443 8.69634 112.06L23.5529 94.8999" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_49" d="M21.3363 89.15L2.41652 110.343C-0.766781 113.91 1.7632 119.563 6.54648 119.563H20.5063" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_50" d="M79.3291 112.35L57.5027 112.32" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_51" d="M47.3191 118.226L77.6321 118.413" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_52" d="M58.0195 110.257L84.5459 81.8135C84.5459 81.8135 84.5659 81.79 84.5759 81.7767C84.8259 81.4634 88.5592 76.8534 93.1892 76.8534C97.8191 76.8534 99.4891 81.4968 97.9825 85.3335L87.1392 112.913C87.0492 113.14 86.8392 113.296 86.5992 113.316C85.3326 113.42 81.3826 113.65 79.916 112.663C79.6793 112.506 79.596 112.2 79.6893 111.933L86.8826 91.8067C87.1159 91.15 86.2792 90.65 85.8126 91.1633L66.7961 112.103" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_53" d="M79.6888 111.93L76.3055 122.57C76.3055 122.57 75.2722 125.223 78.6655 125.223H96.802C96.802 125.223 100.709 124.853 100.709 121.83C100.709 121.83 100.929 120.356 99.4553 119.323C97.982 118.29 87.1387 112.91 87.1387 112.91" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_54" d="M86.7759 117.406C86.7759 117.406 87.7359 114.68 89.3558 114.236" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_55" d="M91.1255 119.546C91.1255 119.546 92.0855 116.82 93.7055 116.376" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_56" d="M99.9724 119.473C99.9724 119.473 129.465 119.503 132.025 119.503C134.585 119.503 134.679 115.563 134.679 115.563V99.0066C134.679 99.0066 134.309 95.0999 131.285 95.0999C131.285 95.0999 129.812 94.8798 128.779 96.3531C127.745 97.8265 121.206 111.953 121.206 111.953C121.206 111.953 121.942 117.703 121.132 119.4" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_57" d="M126.865 109.033C126.865 109.033 124.139 108.073 123.695 106.453" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_58" d="M129.002 104.683C129.002 104.683 126.275 103.723 125.832 102.103" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_59" d="M87.6592 112.026H121.209" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_60" d="M57.946 98.6798C57.946 98.6798 59.496 103.25 63.3293 104.283" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_61" d="M63.6223 97.5C63.6223 97.5 64.729 99.8598 66.8656 100.596" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_62" d="M61.3359 69.4835C61.3359 69.4835 76.6291 70.8602 76.6291 90.3" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_63" d="M63.4023 65.5004C63.4023 65.5004 82.2921 70.2236 82.2921 84.2302" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_64" d="M54.3392 97.91C53.2292 97.0534 52.5159 95.7099 52.5159 94.1999C52.5159 91.6133 54.6125 89.5167 57.1992 89.5167C59.7858 89.5167 61.8824 91.6133 61.8824 94.1999C61.8824 95.8966 60.9824 97.38 59.6325 98.2033" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_65" d="M69.5392 90.2333C69.3525 90.2599 69.1625 90.2733 68.9692 90.2733C66.7492 90.2733 64.9492 88.4733 64.9492 86.2533C64.9492 84.4567 66.1292 82.9334 67.7592 82.42" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_66" d="M60.6391 77.7201C60.7091 78.0468 60.7491 78.3869 60.7491 78.7369C60.7491 81.3635 58.6191 83.4935 55.9925 83.4935C53.3658 83.4935 51.2358 81.3635 51.2358 78.7369C51.2358 76.8469 52.3392 75.2136 53.9358 74.4469" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_67" d="M6.34354 80.8467C6.31354 80.6267 6.29688 80.4001 6.29688 80.1701C6.29688 77.5434 8.42685 75.4135 11.0535 75.4135C13.6801 75.4135 15.8101 77.5434 15.8101 80.1701C15.8101 82.6534 13.9101 84.69 11.4835 84.9067" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_68" d="M18.9128 64.6603C19.5162 65.4703 19.8762 66.4769 19.8762 67.5636C19.8762 70.2469 17.6995 72.4235 15.0162 72.4235C12.3329 72.4235 10.1562 70.2469 10.1562 67.5636C10.1562 67.2769 10.1829 66.9935 10.2296 66.7202" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -<path id="Vector_69" d="M41.6829 69.8403C41.2429 72.0703 39.2729 73.7535 36.9129 73.7535C34.2296 73.7535 32.053 71.5768 32.053 68.8935C32.053 68.2402 32.183 67.617 32.4163 67.0503" stroke="#002140" stroke-width="0.99999" stroke-linecap="round" stroke-linejoin="round"/> -</g> +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 28.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 139 128" style="enable-background:new 0 0 139 128;" xml:space="preserve"> +<style type="text/css"> + .st0{fill:#FFFAF0;} + .st1{fill:#03D47C;} + .st2{fill:#F68DFE;} + .st3{fill:#E4BC07;} + .st4{fill:#FED607;} + .st5{fill:#003C73;} + .st6{fill:#002140;} + .st7{fill:none;stroke:#002140;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;} +</style> +<path class="st0" d="M22.46,119.21c0,0,0.663,1.77,2.323,1.77s4.093,0.11,4.093,0.11l1.55,1.326c0,0,1.217,4.204,4.533,3.76 + c3.317-0.443,3.097-2.986,3.097-2.986s2.877-0.664,3.317-2.987c0,0,2.653-0.443,2.877-2.543c0,0,4.54,1.526,10.453-1.394 + c5.913-2.92,4.81-9.223,4.81-9.223s-2.433-6.303-3.207-6.857c-0.773-0.553-2.987-2.433-2.987-2.433l-29.863-8.737l9.18,20.903 + C32.636,109.92,19.253,110.803,22.46,119.21z"/> +<path class="st1" d="M6.2,76.517c0,0-0.333,6.857,11.06,10.507C28.653,90.674,51.436,97.2,51.436,97.2s16.7,3.65,19.133-5.197 + c2.433-8.847-1.94-11.113-5.197-13.16c0,0-14.71-8.073-29.973-11.613c-15.263-3.54-23.116-3.98-27.316,2.877 + c-0.883,2.433-1.99,5.42-1.88,6.413L6.2,76.517z"/> +<path class="st0" d="M28.763,85.367c3.605,0,6.527-2.922,6.527-6.526s-2.922-6.527-6.527-6.527c-3.604,0-6.527,2.922-6.527,6.527 + S25.159,85.367,28.763,85.367z"/> +<path class="st0" d="M42.036,89.79c3.604,0,6.527-2.922,6.527-6.527c0-3.605-2.922-6.526-6.527-6.526s-6.527,2.922-6.527,6.526 + C35.509,86.868,38.431,89.79,42.036,89.79z"/> +<path class="st2" d="M36.836,52.187c0,0-5.823-5.603-10.397,1.033c-4.57,6.637,3.097,12.093,3.097,12.093L41.186,69l7.817-0.59 + c0,0-4.423-3.097-1.327-6.047c3.097-2.95,5.16,2.803,8.997,1.033c3.833-1.77,3.393-8.997-0.883-11.797l-1.623,1.18l-0.883-45.42 + c0,0-1.473-6.783-8.847-6.34c-7.373,0.443-7.817,4.423-8.257,8.407c-0.443,3.98,0.663,42.763,0.663,42.763L36.836,52.187z"/> +<path class="st3" d="M36.836,52.187c0,0,0.027,0.02,0.073,0.06c0.637,0.523,5.27,4.23,8.663,3.92c3.65-0.333,8.073-3.43,8.073-3.43 + l0.22-4.977c0,0-7.853-2.543-17.033-0.11v4.533L36.836,52.187z"/> +<path class="st4" d="M81.076,112.793l-3.65,10.397c0,0-0.333,1.88,2.877,1.88h18.803c0,0,2.764-1.217,2.877-2.987 + c0.11-1.77-0.663-2.877-0.663-2.877l-12.387-5.973l-7.853-0.443L81.076,112.793z"/> +<path class="st4" d="M122.992,111.356v7.744h11.17c0,0,2.1-1.217,2.1-3.98V98.64c0,0-0.883-3.317-3.096-3.54 + c-2.214-0.223-3.43,1.77-3.43,1.77l-6.747,14.49L122.992,111.356z"/> +<path class="st5" d="M25.04,94.583l-2.507-5.457L2.92,111.1c0,0-0.517,3.76,0.22,5.31c0.737,1.55,4.13,2.95,4.13,2.95l14.747-0.147 + c0,0-1.4-4.647,1.107-6.34l-12.757-0.147l-0.81-0.443L25.04,94.587V94.583z"/> +<path class="st5" d="M47.603,118.62h31.41l1.917-6.49l-22.12,0.146c0,0-6.047,5.16-7.963,5.384 + C48.93,117.883,47.603,118.62,47.603,118.62z"/> +<path class="st5" d="M88.929,113.016l12.276,6.527h21.234c0,0,0.773-6.193-0.111-7.743H89.039L88.929,113.016z"/> +<path class="st5" d="M80.929,112.13c0,0,5.457,1.88,8,0.883l10.727-28.646c0,0,1.66-6.193-3.65-7.52 + c-5.31-1.327-11.503,6.193-11.503,6.193l-24.996,26.433l-0.7,2.804l9.99-0.037l18.58-21.346c0,0,1.217-0.553,0.883,1.217 + c-0.333,1.77-7.337,20.02-7.337,20.02H80.929z"/> +<path class="st5" d="M77.869,88.683l-0.663,2.653l5.087-4.423l1.55-4.093c0,0-1.88-15.04-19.796-17.253c0,0-1.77,0.883-1.327,3.54 + c0,0,15.297,2.723,15.153,19.576H77.869z"/> +<path class="st0" d="M52.983,63.137c0,0-0.997-1.66-3.207-1.55c-2.213,0.11-2.987,1.55-2.987,2.433c0,0.883,1.327,4.313,6.083,5.31 + c4.757,0.997,9.51,0.22,9.51,0.22s-0.553-3.54,3.54-4.867c4.093-1.327,8.883-3.353,8.96-4.647c0.073-1.29-0.883-2.877-1.99-2.653 + c0,0,1.217-2.543-0.663-3.317c-1.88-0.773-5.75,0.443-7.853,3.76c0,0-0.773-7.41-5.197-8.96c-2.763-1.217-3.317,2.543-3.317,2.543 + s6.747,6.527,2.1,11.17c-2.737,2.047-4.977,0.553-4.977,0.553L52.983,63.137z"/> +<path class="st6" d="M42.81,105.383c0,0-0.333,1.327-1.88,0.663c-1.55-0.663-1.437-2.543-1.437-2.543s-2.803,1.55-3.097-0.22 + c-0.293-1.77-0.883-1.55,0.333-2.763c1.217-1.214,2.653-3.54,2.653-3.54s-2.763-2.037-4.203-1.66c-1.44,0.377-3.54,2.323-3.54,2.987 + c0,0.663-0.22,7.299,8.293,9.733c8.517,2.433,9.623-0.334,10.177-1.66c0.553-1.327,1.66-2.654-0.443-3.65 + c-2.1-0.997-3.87-1.55-3.87-1.55L42.81,105.383z"/> +<path class="st6" d="M37.989,53.04l1.77,1.547l7.003-7.593l-3.833,0.59L37.989,53.04z"/> +<path class="st6" d="M42.636,55.84l3.463,0.15l7.67-7.963l-3.17-0.37L42.636,55.84z"/> +<path class="st6" d="M20,64.54c0,0-6.12,0.737-8.257,2.063c0,0,0.147,7.52,6.193,5.677s2.95-7.667,2.063-7.743V64.54z"/> +<path class="st6" d="M13.583,75.38c0,0-4.94,0-5.9,4.72c0,0,2.877,3.98,4.94,4.793c0,0,4.057-0.073,4.57-3.613 + c0.517-3.54-1.99-5.823-3.613-5.9H13.583z"/> +<path class="st6" d="M43.003,69.704c0,0-7.667-2.803-8.997-2.727c-1.327,0.073-1.107,5.677,3.243,6.71 + c4.35,1.033,5.75-3.98,5.75-3.98L43.003,69.704z"/> +<path class="st6" d="M62.322,77.667l-6.93-3.317c0,0-2.213,1.953-2.433,3.17c-0.22,1.217,0.37,5.53,3.613,5.9 + c3.243,0.37,5.9-2.36,5.75-5.75V77.667z"/> +<path class="st6" d="M69.029,82.46c0,0-2.727,0.737-2.58,3.907c0.147,3.17,4.497,3.833,4.497,3.833s2.213-5.973-1.917-7.743V82.46z" + /> +<path class="st6" d="M56.052,97.943l4.94,0.443c0,0,3.17-2.507,2.507-4.867c-0.663-2.36-2.58-4.277-5.603-4.057 + c-3.023,0.22-3.637,3.227-3.76,3.98c-0.59,1.99,1.4,4.35,1.917,4.497V97.943z"/> +<path class="st6" d="M36.146,101.703l3.467-4.57c0,0-4.35-3.467-7.003-0.147c-2.653,3.316,1.697,7.963,1.697,7.963 + s5.9,4.35,10.323,4.496c4.423,0.147,5.823-3.316,5.823-3.316l-0.22-2.58l-4.277-2.507l-3.98,5.237c0,0-1.107-0.074-1.77-0.294 + c-0.663-0.22-0.443-2.653-0.443-2.653s-2.543,0.92-3.04,0.424c-0.497-0.497-0.57-2.047-0.57-2.047L36.146,101.703z"/> +<path class="st5" d="M59.446,98.607c0,0,2.507,5.456,4.94,5.75l3.907-3.614l-3.317-3.54l-5.53,1.4V98.607z"/> +<path class="st7" d="M31.797,56.58c0,0,12.387,10.397,23.556,1.217"/> +<path class="st7" d="M44.403,70.07c-9.97-2.957-31.536-11.853-37.32,2.15c-3.72,9.003,5.47,13.4,12.553,15.61 + c9.643,3.01,19.283,6.05,29.013,8.77c7.5,2.093,17.193,4.133,21.613-4.207c0.757-1.43,1.27-3.013,1.28-4.63 + c0.03-5.683-6.627-8.846-10.913-11.043C55.456,74.07,49.983,71.724,44.403,70.07z"/> +<path class="st7" d="M28.479,85.777c3.787,0,6.857-3.02,6.857-6.747c0-3.726-3.07-6.747-6.857-6.747s-6.857,3.021-6.857,6.747 + C21.623,82.756,24.693,85.777,28.479,85.777z"/> +<path class="st6" d="M25.75,78.183l4.137,0.26c0.123-2.443-1.323-4.58-3.437-4.917c-2.293-0.363-4.497,1.527-4.927,4.22 + c-0.43,2.693,1.083,5.173,3.377,5.537c1.337,0.213,2.64-0.343,3.58-1.373l-2.733-3.727H25.75z"/> +<path class="st7" d="M41.896,90.053c3.787,0,6.857-3.021,6.857-6.747c0-3.726-3.07-6.747-6.857-6.747s-6.857,3.021-6.857,6.747 + C35.039,87.033,38.109,90.053,41.896,90.053z"/> +<path class="st6" d="M39.169,82.46l4.137,0.26c0.123-2.443-1.323-4.58-3.437-4.917c-2.293-0.363-4.497,1.527-4.927,4.22 + c-0.427,2.693,1.083,5.173,3.377,5.537c1.337,0.213,2.64-0.343,3.58-1.373l-2.733-3.727H39.169z"/> +<path class="st7" d="M49.233,68.34c0,0-5.77,0.573-8.213,0.673"/> +<path class="st7" d="M36.883,52.377c0,0-0.85-41-0.85-42.35c0-1.35-0.587-8.633,8.373-8.746c7.52,0,8.96,4.093,8.96,7.63 + c-0.11,1.437,0.547,43.906,0.547,43.906"/> +<path class="st7" d="M36.993,47.51c0,0,10.727-1.217,16.48,0.11"/> +<path class="st7" d="M37.843,53.187l5.603-6.12"/> +<path class="st7" d="M39.979,54.514l7.01-7.51"/> +<path class="st7" d="M43.003,55.547l7.743-8.343"/> +<path class="st7" d="M45.953,56.064l7.743-8.343"/> +<path class="st7" d="M65.493,60.78c3.98-2.213,8.7-4.35,9.51-1.843c0.81,2.507-5.16,4.793-9.363,6.12 + c-4.203,1.327-2.803,4.423-2.803,4.423s-3.98,1.033-7.647,0.6c-2.647-0.31-6.12-1.367-7.843-3.557c-1.84-2.34,0.83-6.043,3.63-4.857 + c0.667,0.283,1.19,0.817,1.78,1.237c1.523,1.08,3.97,1.49,5.367,0c2.723-2.9,1.173-9.52-2.213-11.307"/> +<path class="st7" d="M72.642,57.757c0,0,1.55-2.137-0.073-3.467c-1.623-1.327-6.413-0.147-8.48,4.423 + c0.463-1.023-0.193-3.37-0.427-4.377c-0.64-2.773-2.497-5.72-5.707-5.63c-1.637,0.043-2.273,1.423-2.05,2.893 + c-1.66,0.553-2.987,3.54-9.733,4.423c-6.747,0.883-8.737-4.313-12.94-5.53c-4.203-1.217-7.077,1.99-7.87,4.41 + c-0.993,2.407-0.967,7.57,5.033,11.01"/> +<path class="st7" d="M32.13,109.776l-9.513-21.013"/> +<path class="st7" d="M44.182,117.85c0,0,1.217,0.553,3.87,0.443c2.653-0.11,11.503-2.1,11.503-9.51c0-7.41-6.527-11.06-6.527-11.06" + /> +<path class="st7" d="M31.796,100.596c0,0,0.443,3.98,6.413,6.857c2.807,1.35,7.977,3.19,10.77,0.81 + c1.49-1.27,2.397-4.14,0.513-5.453c-1.603-1.117-3.483-1.744-5.247-2.57c-1.38-0.647-2.72-1.417-3.82-2.487 + c-1.673-1.63-4.317-3.03-6.64-1.863c-1.73,0.87-2.183,2.933-1.987,4.71L31.796,100.596z"/> +<path class="st7" d="M39.373,97.223c0,0-1.383,2.047-2.71,3.707c-1.327,1.66-0.387,2.49,0.057,2.82c0.443,0.333,1.697,0.7,3.04-1.2 + c1.347-1.9,2.323-3.28,2.323-3.28"/> +<path class="st7" d="M42.839,99.74c-0.417,0.607-1.49,2.143-2.53,3.44c-1.327,1.66-0.387,2.49,0.057,2.82 + c0.443,0.333,1.697,0.7,3.04-1.2c1.347-1.9,2.323-3.28,2.323-3.28"/> +<path class="st7" d="M37.99,123.38c0,0,1.88,0.166,2.877-1.16c0.997-1.327,0.443-4.867-4.203-7.19"/> +<path class="st7" d="M41.516,120.486c0.64-0.07,1.603-0.316,2.227-1.143c0.997-1.327,0.443-4.867-4.203-7.19 + c0,0-4.147-1.99-6.913-1.99c-6.747,0-11.17,2.877-11.117,6.637c0,0,0.167,4.48,3.65,4.37s4.423-0.22,5.253,1.05 + c0.83,1.273,1.99,4.313,4.257,4.313c2.267,0,3.097-1.16,3.097-2.877c0-1.716-2.82-4.756-5.363-6.083"/> +<path class="st7" d="M22.95,113.04H10.643c-0.507,0-0.78-0.597-0.447-0.98L25.053,94.9"/> +<path class="st7" d="M22.836,89.15l-18.92,21.193c-3.183,3.567-0.653,9.22,4.13,9.22h13.96"/> +<path class="st7" d="M80.829,112.35l-21.826-0.03"/> +<path class="st7" d="M48.819,118.226l30.313,0.187"/> +<path class="st7" d="M59.52,110.257l26.526-28.443c0,0,0.02-0.023,0.03-0.037c0.25-0.313,3.983-4.923,8.613-4.923 + s6.3,4.643,4.793,8.48l-10.843,27.579c-0.09,0.227-0.3,0.384-0.54,0.404c-1.267,0.103-5.217,0.333-6.683-0.654 + c-0.237-0.156-0.32-0.463-0.227-0.73l7.193-20.126c0.233-0.657-0.603-1.157-1.07-0.643l-19.016,20.94"/> +<path class="st7" d="M81.189,111.93l-3.383,10.64c0,0-1.033,2.653,2.36,2.653h18.137c0,0,3.907-0.37,3.907-3.393 + c0,0,0.22-1.474-1.254-2.507c-1.473-1.033-12.316-6.413-12.316-6.413"/> +<path class="st7" d="M88.276,117.406c0,0,0.96-2.726,2.58-3.169"/> +<path class="st7" d="M92.625,119.546c0,0,0.96-2.726,2.58-3.17"/> +<path class="st7" d="M101.472,119.473c0,0,29.493,0.03,32.053,0.03c2.56,0,2.654-3.94,2.654-3.94V99.007c0,0-0.37-3.907-3.394-3.907 + c0,0-1.473-0.22-2.506,1.253c-1.034,1.473-7.574,15.6-7.574,15.6s0.737,5.75-0.073,7.447"/> +<path class="st7" d="M128.365,109.033c0,0-2.726-0.96-3.17-2.58"/> +<path class="st7" d="M130.502,104.683c0,0-2.727-0.96-3.17-2.58"/> +<path class="st7" d="M89.159,112.026h33.55"/> +<path class="st7" d="M59.446,98.68c0,0,1.55,4.57,5.383,5.603"/> +<path class="st7" d="M65.122,97.5c0,0,1.107,2.36,3.243,3.096"/> +<path class="st7" d="M62.836,69.484c0,0,15.293,1.377,15.293,20.817"/> +<path class="st7" d="M64.902,65.5c0,0,18.89,4.723,18.89,18.73"/> +<path class="st7" d="M55.839,97.91c-1.11-0.857-1.823-2.2-1.823-3.71c0-2.587,2.097-4.683,4.683-4.683 + c2.587,0,4.683,2.097,4.683,4.683c0,1.697-0.9,3.18-2.25,4.003"/> +<path class="st7" d="M71.039,90.233c-0.187,0.027-0.377,0.04-0.57,0.04c-2.22,0-4.02-1.8-4.02-4.02c0-1.797,1.18-3.32,2.81-3.833"/> +<path class="st7" d="M62.139,77.72c0.07,0.327,0.11,0.667,0.11,1.017c0,2.627-2.13,4.757-4.757,4.757 + c-2.627,0-4.757-2.13-4.757-4.757c0-1.89,1.103-3.523,2.7-4.29"/> +<path class="st7" d="M7.844,80.847c-0.03-0.22-0.047-0.447-0.047-0.677c0-2.627,2.13-4.757,4.757-4.757 + c2.627,0,4.757,2.13,4.757,4.757c0,2.483-1.9,4.52-4.327,4.737"/> +<path class="st7" d="M20.413,64.66c0.603,0.81,0.963,1.817,0.963,2.903c0,2.683-2.177,4.86-4.86,4.86s-4.86-2.177-4.86-4.86 + c0-0.287,0.027-0.57,0.073-0.843"/> +<path class="st7" d="M43.183,69.84c-0.44,2.23-2.41,3.913-4.77,3.913c-2.683,0-4.86-2.177-4.86-4.86c0-0.653,0.13-1.277,0.363-1.843 + "/> </svg> From 1c4111b5f2c8621c2b6fe26e5a91e39672c0e0e0 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 10:51:56 -0800 Subject: [PATCH 72/81] Prepopulate reason form with draft reason --- .../settings/ExitSurvey/ExitSurveyReasonPage.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index b62deb323ac7..101ab3545ae6 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,4 +1,5 @@ import React, {useMemo, useState} from 'react'; +import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -19,12 +20,16 @@ import INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import ExitSurveyOffline from './ExitSurveyOffline'; -function ExitSurveyReasonPage() { +type ExitSurveyReasonPageOnyxProps = { + draftReason?: ExitReason; +}; + +function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); - const [reason, setReason] = useState<ExitReason>(); + const [reason, setReason] = useState<ExitReason | undefined>(draftReason); const reasons: Choice[] = useMemo( () => Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ @@ -82,4 +87,9 @@ function ExitSurveyReasonPage() { ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; -export default ExitSurveyReasonPage; +export default withOnyx<ExitSurveyReasonPageOnyxProps, ExitSurveyReasonPageOnyxProps>({ + draftReason: { + key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, + selector: (value) => value?.[INPUT_IDS.REASON], + }, +})(ExitSurveyReasonPage); From 043601d48c1b6a2c364d17269f962fad642f86be Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 10:59:24 -0800 Subject: [PATCH 73/81] Remove outdated cleanup --- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 2 +- src/pages/settings/{ => AboutPage}/AboutPage.tsx | 2 +- src/pages/settings/{ => AboutPage}/ConsolePage.tsx | 0 .../settings/{ => AboutPage}/ShareLogList/BaseShareLogList.tsx | 0 .../settings/{ => AboutPage}/ShareLogList/index.native.tsx | 0 src/pages/settings/{ => AboutPage}/ShareLogList/index.tsx | 0 src/pages/settings/{ => AboutPage}/ShareLogList/types.ts | 0 src/pages/settings/{ => AboutPage}/ShareLogPage.tsx | 0 src/pages/settings/{ => AboutPage}/TroubleshootPage.tsx | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename src/pages/settings/{ => AboutPage}/AboutPage.tsx (99%) rename src/pages/settings/{ => AboutPage}/ConsolePage.tsx (100%) rename src/pages/settings/{ => AboutPage}/ShareLogList/BaseShareLogList.tsx (100%) rename src/pages/settings/{ => AboutPage}/ShareLogList/index.native.tsx (100%) rename src/pages/settings/{ => AboutPage}/ShareLogList/index.tsx (100%) rename src/pages/settings/{ => AboutPage}/ShareLogList/types.ts (100%) rename src/pages/settings/{ => AboutPage}/ShareLogPage.tsx (100%) rename src/pages/settings/{ => AboutPage}/TroubleshootPage.tsx (100%) diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 713e7876da31..00212ce17818 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -193,7 +193,7 @@ const AccountSettingsModalStackNavigator = createModalStackNavigator( [SCREENS.SETTINGS.PROFILE.ROOT]: () => require('../../../pages/settings/Profile/ProfilePage').default as React.ComponentType, [SCREENS.SETTINGS.SHARE_CODE]: () => require('../../../pages/ShareCodePage').default as React.ComponentType, [SCREENS.SETTINGS.WALLET.ROOT]: () => require('../../../pages/settings/Wallet/WalletPage').default as React.ComponentType, - [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage').default as React.ComponentType, + [SCREENS.SETTINGS.ABOUT]: () => require('../../../pages/settings/AboutPage/AboutPage').default as React.ComponentType, }, (styles) => ({cardStyle: styles.navigationScreenCardStyle, headerShown: false}), ); diff --git a/src/pages/settings/AboutPage.tsx b/src/pages/settings/AboutPage/AboutPage.tsx similarity index 99% rename from src/pages/settings/AboutPage.tsx rename to src/pages/settings/AboutPage/AboutPage.tsx index f82213ab2d44..3346b044ceca 100644 --- a/src/pages/settings/AboutPage.tsx +++ b/src/pages/settings/AboutPage/AboutPage.tsx @@ -25,7 +25,7 @@ import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import type IconAsset from '@src/types/utils/IconAsset'; -import pkg from '../../../package.json'; +import pkg from '../../../../package.json'; function getFlavor(): string { const bundleId = DeviceInfo.getBundleId(); diff --git a/src/pages/settings/ConsolePage.tsx b/src/pages/settings/AboutPage/ConsolePage.tsx similarity index 100% rename from src/pages/settings/ConsolePage.tsx rename to src/pages/settings/AboutPage/ConsolePage.tsx diff --git a/src/pages/settings/ShareLogList/BaseShareLogList.tsx b/src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx similarity index 100% rename from src/pages/settings/ShareLogList/BaseShareLogList.tsx rename to src/pages/settings/AboutPage/ShareLogList/BaseShareLogList.tsx diff --git a/src/pages/settings/ShareLogList/index.native.tsx b/src/pages/settings/AboutPage/ShareLogList/index.native.tsx similarity index 100% rename from src/pages/settings/ShareLogList/index.native.tsx rename to src/pages/settings/AboutPage/ShareLogList/index.native.tsx diff --git a/src/pages/settings/ShareLogList/index.tsx b/src/pages/settings/AboutPage/ShareLogList/index.tsx similarity index 100% rename from src/pages/settings/ShareLogList/index.tsx rename to src/pages/settings/AboutPage/ShareLogList/index.tsx diff --git a/src/pages/settings/ShareLogList/types.ts b/src/pages/settings/AboutPage/ShareLogList/types.ts similarity index 100% rename from src/pages/settings/ShareLogList/types.ts rename to src/pages/settings/AboutPage/ShareLogList/types.ts diff --git a/src/pages/settings/ShareLogPage.tsx b/src/pages/settings/AboutPage/ShareLogPage.tsx similarity index 100% rename from src/pages/settings/ShareLogPage.tsx rename to src/pages/settings/AboutPage/ShareLogPage.tsx diff --git a/src/pages/settings/TroubleshootPage.tsx b/src/pages/settings/AboutPage/TroubleshootPage.tsx similarity index 100% rename from src/pages/settings/TroubleshootPage.tsx rename to src/pages/settings/AboutPage/TroubleshootPage.tsx From 3dfac290bfbaf12b98d9f7af41507f7a149db2f0 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 11:02:17 -0800 Subject: [PATCH 74/81] Fix comment in NumberUtils --- src/libs/NumberUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/NumberUtils.ts b/src/libs/NumberUtils.ts index aaef0b5f8dfa..60e5246f5ed2 100644 --- a/src/libs/NumberUtils.ts +++ b/src/libs/NumberUtils.ts @@ -70,7 +70,7 @@ function parseFloatAnyLocale(value: string): number { } /** - * Given an input number q and another number n, returns the largest number x that's less than p and divisible by q. + * Given an input number p and another number q, returns the largest number that's less than p and divisible by q. */ function roundDownToLargestMultiple(p: number, q: number) { return Math.floor(p / q) * q; From c7f2b5721b374eebedaed5dee5ee3508247f2652 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 11:59:10 -0800 Subject: [PATCH 75/81] fix draft on reason page --- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 101ab3545ae6..4d94c035697b 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -21,7 +21,7 @@ import type {Errors} from '@src/types/onyx/OnyxCommon'; import ExitSurveyOffline from './ExitSurveyOffline'; type ExitSurveyReasonPageOnyxProps = { - draftReason?: ExitReason; + draftReason: ExitReason | null; }; function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { @@ -29,7 +29,7 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { const styles = useThemeStyles(); const {isOffline} = useNetwork(); - const [reason, setReason] = useState<ExitReason | undefined>(draftReason); + const [reason, setReason] = useState<ExitReason | null>(draftReason); const reasons: Choice[] = useMemo( () => Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ @@ -90,6 +90,6 @@ ExitSurveyReasonPage.displayName = 'ExitSurveyReasonPage'; export default withOnyx<ExitSurveyReasonPageOnyxProps, ExitSurveyReasonPageOnyxProps>({ draftReason: { key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM_DRAFT, - selector: (value) => value?.[INPUT_IDS.REASON], + selector: (value) => value?.[INPUT_IDS.REASON] ?? null, }, })(ExitSurveyReasonPage); From 22b63905fa23b83c21208eb6d707dd8c86d4d1a7 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 12:29:34 -0800 Subject: [PATCH 76/81] Setup RadioButtons to work as a controlled input --- src/components/RadioButtons.tsx | 17 +++++++++++++++-- .../ExitSurvey/ExitSurveyReasonPage.tsx | 12 +++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/components/RadioButtons.tsx b/src/components/RadioButtons.tsx index c15fe65b357a..90c7d8580b5c 100644 --- a/src/components/RadioButtons.tsx +++ b/src/components/RadioButtons.tsx @@ -1,4 +1,4 @@ -import React, {forwardRef, useState} from 'react'; +import React, {forwardRef, useEffect, useState} from 'react'; import type {ForwardedRef} from 'react'; import {View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; @@ -28,11 +28,23 @@ type RadioButtonsProps = { /** Style for radio button */ radioButtonStyle?: StyleProp<ViewStyle>; + + /** Callback executed when input value changes (same as onPress, but required by FormProvider for the sake of saving drafts) */ + onInputChange?: (value: string) => void; + + /** The checked value, if you're using this component as a controlled input. */ + value?: string; }; -function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText}: RadioButtonsProps, ref: ForwardedRef<View>) { +function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyle, errorText, onInputChange = () => {}, value}: RadioButtonsProps, ref: ForwardedRef<View>) { const styles = useThemeStyles(); const [checkedValue, setCheckedValue] = useState(defaultCheckedValue); + useEffect(() => { + if (value === checkedValue) { + return; + } + setCheckedValue(value ?? ''); + }, [checkedValue, value]); return ( <> @@ -47,6 +59,7 @@ function RadioButtons({items, onPress, defaultCheckedValue = '', radioButtonStyl style={[styles.mb4, radioButtonStyle]} onPress={() => { setCheckedValue(item.value); + onInputChange(item.value); return onPress(item.value); }} label={item.label} diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index 4d94c035697b..a74a7726d638 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -1,4 +1,4 @@ -import React, {useMemo, useState} from 'react'; +import React, {useEffect, useMemo, useState} from 'react'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -30,6 +30,14 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { const {isOffline} = useNetwork(); const [reason, setReason] = useState<ExitReason | null>(draftReason); + useEffect(() => { + // disabling lint because || is fine to use as a logical operator (as opposed to being used to define a default value) + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (reason || !draftReason) { + return; + } + setReason(draftReason); + }, [reason, draftReason]); const reasons: Choice[] = useMemo( () => Object.values(CONST.EXIT_SURVEY.REASONS).map((value) => ({ @@ -75,8 +83,10 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { <InputWrapper InputComponent={RadioButtons} inputID={INPUT_IDS.REASON} + value={reason as string} items={reasons} onPress={(value) => setReason(value as ExitReason)} + shouldSaveDraft /> </> )} From 7be4f4658cb8b70c9239de0b479c12272b247806 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Wed, 14 Feb 2024 13:39:38 -0800 Subject: [PATCH 77/81] Switch backTo for response and confirm page if you're offline --- src/libs/Navigation/types.ts | 5 ++- .../ExitSurvey/ExitSurveyConfirmPage.tsx | 34 ++++++++++++++++--- .../ExitSurvey/ExitSurveyResponsePage.tsx | 22 +++++++++--- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 6e1942f8a8fb..d656daf3580d 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -172,8 +172,11 @@ type SettingsNavigatorParamList = { [SCREENS.SETTINGS.EXIT_SURVEY.REASON]: undefined; [SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE]: { [EXIT_SURVEY_REASON_FORM_INPUT_IDS.REASON]: ValueOf<typeof CONST.EXIT_SURVEY.REASONS>; + backTo: Routes; + }; + [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: { + backTo: Routes; }; - [SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM]: undefined; } & ReimbursementAccountNavigatorParamList; type NewChatNavigatorParamList = { diff --git a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx index 15f9becd6580..7459819afd99 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyConfirmPage.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useCallback, useEffect} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; @@ -13,12 +14,14 @@ import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@navigation/Navigation'; +import type {SettingsNavigatorParamList} from '@navigation/types'; import variables from '@styles/variables'; import * as ExitSurvey from '@userActions/ExitSurvey'; import * as Link from '@userActions/Link'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; import type {ExitReason, ExitSurveyReasonForm} from '@src/types/form/ExitSurveyReasonForm'; import EXIT_SURVEY_REASON_INPUT_IDS from '@src/types/form/ExitSurveyReasonForm'; import ExitSurveyOffline from './ExitSurveyOffline'; @@ -28,15 +31,38 @@ type ExitSurveyConfirmPageOnyxProps = { isLoading: OnyxEntry<boolean>; }; -function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOnyxProps) { +type ExitSurveyConfirmPageProps = ExitSurveyConfirmPageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.CONFIRM>; + +function ExitSurveyConfirmPage({exitReason, isLoading, route, navigation}: ExitSurveyConfirmPageProps) { const {translate} = useLocalize(); const {isOffline} = useNetwork(); const styles = useThemeStyles(); + + const getBackToParam = useCallback(() => { + if (isOffline) { + return ROUTES.SETTINGS; + } + if (exitReason) { + return ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON); + } + return ROUTES.SETTINGS; + }, [exitReason, isOffline]); + const {backTo} = route.params; + useEffect(() => { + const newBackTo = getBackToParam(); + if (backTo === newBackTo) { + return; + } + navigation.setParams({ + backTo: newBackTo, + }); + }, [backTo, getBackToParam, navigation]); + return ( <ScreenWrapper testID={ExitSurveyConfirmPage.displayName}> <HeaderWithBackButton title={translate('exitSurvey.header')} - onBackButtonPress={() => Navigation.goBack(exitReason ? ROUTES.SETTINGS_EXIT_SURVEY_RESPONSE.getRoute(exitReason, ROUTES.SETTINGS_EXIT_SURVEY_REASON) : undefined)} + onBackButtonPress={() => Navigation.goBack(backTo)} /> <View style={[styles.flex1, styles.justifyContentCenter, styles.alignItemsCenter, styles.mh5]}> {isOffline && <ExitSurveyOffline />} @@ -70,7 +96,7 @@ function ExitSurveyConfirmPage({exitReason, isLoading}: ExitSurveyConfirmPageOny ExitSurveyConfirmPage.displayName = 'ExitSurveyConfirmPage'; -export default withOnyx<ExitSurveyConfirmPageOnyxProps, ExitSurveyConfirmPageOnyxProps>({ +export default withOnyx<ExitSurveyConfirmPageProps, ExitSurveyConfirmPageOnyxProps>({ exitReason: { key: ONYXKEYS.FORMS.EXIT_SURVEY_REASON_FORM, selector: (value: OnyxEntry<ExitSurveyReasonForm>) => value?.[EXIT_SURVEY_REASON_INPUT_IDS.REASON], diff --git a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx index 9a23e3406521..c43ef8dd9320 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyResponsePage.tsx @@ -1,5 +1,5 @@ import type {StackScreenProps} from '@react-navigation/stack'; -import React, {useCallback} from 'react'; +import React, {useCallback, useEffect} from 'react'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; @@ -36,16 +36,28 @@ type ExitSurveyResponsePageOnyxProps = { type ExitSurveyResponsePageProps = ExitSurveyResponsePageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.SETTINGS.EXIT_SURVEY.RESPONSE>; -function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePageProps) { +function ExitSurveyResponsePage({draftResponse, route, navigation}: ExitSurveyResponsePageProps) { const {translate} = useLocalize(); - const {isOffline} = useNetwork(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const {keyboardHeight} = useKeyboardState(); const {windowHeight} = useWindowDimensions(); const {top: safeAreaInsetsTop} = useSafeAreaInsets(); - const {reason} = route.params; + const {reason, backTo} = route.params; + const {isOffline} = useNetwork({ + onReconnect: () => { + navigation.setParams({ + backTo: ROUTES.SETTINGS_EXIT_SURVEY_REASON, + }); + }, + }); + useEffect(() => { + if (!isOffline || backTo === ROUTES.SETTINGS) { + return; + } + navigation.setParams({backTo: ROUTES.SETTINGS}); + }, [backTo, isOffline, navigation]); const submitForm = useCallback(() => { ExitSurvey.saveResponse(draftResponse); @@ -86,7 +98,7 @@ function ExitSurveyResponsePage({draftResponse, route}: ExitSurveyResponsePagePr <ScreenWrapper testID={ExitSurveyResponsePage.displayName}> <HeaderWithBackButton title={translate('exitSurvey.header')} - onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_EXIT_SURVEY_REASON)} + onBackButtonPress={() => Navigation.goBack(backTo)} /> <FormProvider formID={ONYXKEYS.FORMS.EXIT_SURVEY_RESPONSE_FORM} From faf6741acf75c39233bc060b53d2a9e2a91571ad Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 20 Feb 2024 11:01:01 -0800 Subject: [PATCH 78/81] Use stable reference for onPress --- src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx index a74a7726d638..dbaf330803c1 100644 --- a/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx +++ b/src/pages/settings/ExitSurvey/ExitSurveyReasonPage.tsx @@ -85,7 +85,7 @@ function ExitSurveyReasonPage({draftReason}: ExitSurveyReasonPageOnyxProps) { inputID={INPUT_IDS.REASON} value={reason as string} items={reasons} - onPress={(value) => setReason(value as ExitReason)} + onPress={setReason as (value: string) => void} shouldSaveDraft /> </> From a5fb310bf29feddc7de43722e4b14cee9af6470e Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Tue, 20 Feb 2024 11:01:23 -0800 Subject: [PATCH 79/81] Delete gremlin file --- src/types/onyx/Form.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/types/onyx/Form.ts diff --git a/src/types/onyx/Form.ts b/src/types/onyx/Form.ts deleted file mode 100644 index e69de29bb2d1..000000000000 From 6040f50f4b74442d0fa9516d32d40be0232ebee3 Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Sat, 24 Feb 2024 09:14:52 -0800 Subject: [PATCH 80/81] Fix package-lock.json after conflict resolution --- package-lock.json | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6090b2b1c0d2..aab783e8bbb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -237,7 +237,7 @@ "style-loader": "^2.0.0", "time-analytics-webpack-plugin": "^0.1.17", "ts-node": "^10.9.2", - "type-fest": "^3.12.0", + "type-fest": "^4.10.2", "typescript": "^5.3.2", "wait-port": "^0.2.9", "webpack": "^5.76.0", @@ -8139,9 +8139,9 @@ } }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", - "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", + "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", "dev": true, "dependencies": { "ansi-html-community": "^0.0.8", @@ -8161,7 +8161,7 @@ "@types/webpack": "4.x || 5.x", "react-refresh": ">=0.10.0 <1.0.0", "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", + "type-fest": ">=0.17.0 <5.0.0", "webpack": ">=4.43.0 <6.0.0", "webpack-dev-server": "3.x || 4.x", "webpack-hot-middleware": "2.x", @@ -26870,10 +26870,11 @@ } }, "node_modules/core-js-pure": { - "version": "3.24.1", + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.36.0.tgz", + "integrity": "sha512-cN28qmhRNgbMZZMc/RFu5w8pK9VJzpb2rJVR/lHuZJKwmXnoWOpXmMkxqBB514igkp1Hu8WGROsiOAzUcKdHOQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" @@ -38622,6 +38623,17 @@ "node": ">=8" } }, + "node_modules/jest-watch-typeahead/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-watcher": { "version": "29.4.1", "license": "MIT", @@ -50849,11 +50861,12 @@ } }, "node_modules/type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.3.tgz", + "integrity": "sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==", + "dev": true, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" From c98c48fc0e21803294aa182d618985028055ff5b Mon Sep 17 00:00:00 2001 From: rory <rory@expensify.com> Date: Sat, 24 Feb 2024 09:24:22 -0800 Subject: [PATCH 81/81] Fix TS errors --- src/types/form/ExitSurveyReasonForm.ts | 9 ++++++--- src/types/form/ExitSurveyResponseForm.ts | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/types/form/ExitSurveyReasonForm.ts b/src/types/form/ExitSurveyReasonForm.ts index 48f10d4d163d..48eddb026010 100644 --- a/src/types/form/ExitSurveyReasonForm.ts +++ b/src/types/form/ExitSurveyReasonForm.ts @@ -8,9 +8,12 @@ const INPUT_IDS = { REASON: 'reason', } as const; -type ExitSurveyReasonForm = Form<{ - [INPUT_IDS.REASON]: ExitReason; -}>; +type ExitSurveyReasonForm = Form< + ValueOf<typeof INPUT_IDS>, + { + [INPUT_IDS.REASON]: ExitReason; + } +>; export type {ExitSurveyReasonForm, ExitReason}; export default INPUT_IDS; diff --git a/src/types/form/ExitSurveyResponseForm.ts b/src/types/form/ExitSurveyResponseForm.ts index 6776b29b8054..6e3458bd8e38 100644 --- a/src/types/form/ExitSurveyResponseForm.ts +++ b/src/types/form/ExitSurveyResponseForm.ts @@ -1,12 +1,16 @@ +import type {ValueOf} from 'type-fest'; import type Form from './Form'; const INPUT_IDS = { RESPONSE: 'response', } as const; -type ExitSurveyResponseForm = Form<{ - [INPUT_IDS.RESPONSE]: string; -}>; +type ExitSurveyResponseForm = Form< + ValueOf<typeof INPUT_IDS>, + { + [INPUT_IDS.RESPONSE]: string; + } +>; export type {ExitSurveyResponseForm}; export default INPUT_IDS;