diff --git a/.well-known/apple-app-site-association b/.well-known/apple-app-site-association index d6da0232f2fc..1e63fdcb2d52 100644 --- a/.well-known/apple-app-site-association +++ b/.well-known/apple-app-site-association @@ -79,6 +79,10 @@ { "/": "/search/*", "comment": "Search" + }, + { + "/": "/money2020/*", + "comment": "Money 2020" } ] } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d823324f50bf..7419d5b1e1a7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -70,6 +70,7 @@ + @@ -87,6 +88,7 @@ + diff --git a/src/CONST.ts b/src/CONST.ts index 0a86aaf7648a..d1d9bb1dede9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -2718,6 +2718,7 @@ const CONST = { DEMO_PAGES: { SAASTR: 'SaaStrDemoSetup', SBE: 'SbeDemoSetup', + MONEY2020: 'Money2020DemoSetup', }, MAPBOX: { diff --git a/src/Expensify.js b/src/Expensify.js index 642b8ceb456c..6010824cf275 100644 --- a/src/Expensify.js +++ b/src/Expensify.js @@ -29,6 +29,7 @@ import SplashScreenHider from './components/SplashScreenHider'; import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper'; import EmojiPicker from './components/EmojiPicker/EmojiPicker'; import * as EmojiPickerAction from './libs/actions/EmojiPickerAction'; +import * as DemoActions from './libs/actions/DemoActions'; import DeeplinkWrapper from './components/DeeplinkWrapper'; // This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection @@ -167,11 +168,13 @@ function Expensify(props) { // If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report Linking.getInitialURL().then((url) => { + DemoActions.runDemoByURL(url); Report.openReportFromDeepLink(url, isAuthenticated); }); // Open chat report from a deep link (only mobile native) Linking.addEventListener('url', (state) => { + DemoActions.runDemoByURL(state.url); Report.openReportFromDeepLink(state.url, isAuthenticated); }); diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 7127c1483c26..398b9ac6ba4f 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -337,9 +337,10 @@ export default { getRoute: (policyID: string) => `workspace/${policyID}/members`, }, - // These are some on-off routes that will be removed once they're no longer needed (see GH issues for details) + // These are some one-off routes that will be removed once they're no longer needed (see GH issues for details) SAASTR: 'saastr', SBE: 'sbe', + MONEY2020: 'money2020', // Iframe screens from olddot HOME_OLDDOT: 'home', diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.js b/src/libs/Navigation/AppNavigator/AuthScreens.js index 0869306bb491..a4d934faec43 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.js +++ b/src/libs/Navigation/AppNavigator/AuthScreens.js @@ -117,6 +117,13 @@ const propTypes = { /** The last Onyx update ID was applied to the client */ lastUpdateIDAppliedToClient: PropTypes.number, + /** Information about any currently running demos */ + demoInfo: PropTypes.shape({ + money2020: PropTypes.shape({ + isBeginningDemo: PropTypes.bool, + }), + }), + ...windowDimensionsPropTypes, }; @@ -127,6 +134,7 @@ const defaultProps = { }, lastOpenedPublicRoomID: null, lastUpdateIDAppliedToClient: null, + demoInfo: {}, }; class AuthScreens extends React.Component { @@ -169,6 +177,10 @@ class AuthScreens extends React.Component { App.setUpPoliciesAndNavigate(this.props.session, !this.props.isSmallScreenWidth); App.redirectThirdPartyDesktopSignIn(); + // Check if we should be running any demos immediately after signing in. + if (lodashGet(this.props.demoInfo, 'money2020.isBeginningDemo', false)) { + Navigation.navigate(ROUTES.MONEY2020, CONST.NAVIGATION.TYPE.FORCED_UP); + } if (this.props.lastOpenedPublicRoomID) { // Re-open the last opened public room if the user logged in from a public room link Report.openLastOpenedPublicRoom(this.props.lastOpenedPublicRoomID); @@ -299,6 +311,11 @@ class AuthScreens extends React.Component { options={defaultScreenOptions} component={DemoSetupPage} /> + { + currentUserEmail = lodashGet(val, 'email', ''); + }, +}); + +function runMoney2020Demo() { + // Try to navigate to existing demo chat if it exists in Onyx + const money2020AccountID = Number(Config ? Config.EXPENSIFY_ACCOUNT_ID_MONEY2020 : 15864555); + const existingChatReport = ReportUtils.getChatByParticipants([money2020AccountID]); + if (existingChatReport) { + // We must call goBack() to remove the demo route from nav history + Navigation.goBack(); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(existingChatReport.reportID)); + return; + } + + // We use makeRequestWithSideEffects here because we need to get the chat report ID to navigate to it after it's created + // eslint-disable-next-line rulesdir/no-api-side-effects-method + API.makeRequestWithSideEffects('CreateChatReport', { + emailList: `${currentUserEmail},money2020@expensify.com`, + activationConference: 'money2020', + }).then((response) => { + // If there's no response or no reportID in the response, navigate the user home so user doesn't get stuck. + if (!response || !response.reportID) { + Navigation.goBack(); + Navigation.navigate(ROUTES.HOME); + return; + } + + // Get reportID & navigate to it + // Note: We must call goBack() to remove the demo route from history + const chatReportID = response.reportID; + Navigation.goBack(); + Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)); + }); +} + +/** + * Runs code for specific demos, based on the provided URL + * + * @param {String} url - URL user is navigating to via deep link (or regular link in web) + */ +function runDemoByURL(url = '') { + const cleanUrl = (url || '').toLowerCase(); + + if (cleanUrl.endsWith(ROUTES.MONEY2020)) { + Onyx.set(ONYXKEYS.DEMO_INFO, { + money2020: { + isBeginningDemo: true, + }, + }); + } else { + // No demo is being run, so clear out demo info + Onyx.set(ONYXKEYS.DEMO_INFO, null); + } +} + +export {runMoney2020Demo, runDemoByURL}; diff --git a/src/pages/DemoSetupPage.js b/src/pages/DemoSetupPage.js index 5d4b99a0daf9..5432bea0c806 100644 --- a/src/pages/DemoSetupPage.js +++ b/src/pages/DemoSetupPage.js @@ -1,9 +1,11 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import PropTypes from 'prop-types'; import {useFocusEffect} from '@react-navigation/native'; import FullScreenLoadingIndicator from '../components/FullscreenLoadingIndicator'; import Navigation from '../libs/Navigation/Navigation'; import ROUTES from '../ROUTES'; +import CONST from '../CONST'; +import * as DemoActions from '../libs/actions/DemoActions'; const propTypes = { /** Navigation route context info provided by react navigation */ @@ -18,12 +20,16 @@ const propTypes = { * route that led the user here. Now, it's just used to route the user home so we * don't show them a "Hmm... It's not here" message (which looks broken). */ -function DemoSetupPage() { - useFocusEffect(() => { - Navigation.isNavigationReady().then(() => { - Navigation.goBack(ROUTES.HOME); - }); - }); +function DemoSetupPage(props) { + useFocusEffect( + useCallback(() => { + if (props.route.name === CONST.DEMO_PAGES.MONEY2020) { + DemoActions.runMoney2020Demo(); + } else { + Navigation.goBack(ROUTES.HOME); + } + }, [props.route.name]), + ); return ; } diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js index 80fd1d39239d..c87d4f06e1f4 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.js @@ -62,6 +62,13 @@ const propTypes = { /** Forwarded ref to FloatingActionButtonAndPopover */ innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + + /** Information about any currently running demos */ + demoInfo: PropTypes.shape({ + money2020: PropTypes.shape({ + isBeginningDemo: PropTypes.bool, + }), + }), }; const defaultProps = { onHideCreateMenu: () => {}, @@ -70,6 +77,7 @@ const defaultProps = { betas: [], isLoading: false, innerRef: null, + demoInfo: {}, }; /** @@ -152,6 +160,9 @@ function FloatingActionButtonAndPopover(props) { if (currentRoute && ![NAVIGATORS.CENTRAL_PANE_NAVIGATOR, SCREENS.HOME].includes(currentRoute.name)) { return; } + if (lodashGet(props.demoInfo, 'money2020.isBeginningDemo', false)) { + return; + } Welcome.show({routes, showCreateMenu}); // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.isLoading]); @@ -262,6 +273,9 @@ export default compose( isLoading: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, + demoInfo: { + key: ONYXKEYS.DEMO_INFO, + }, }), )( forwardRef((props, ref) => (