Skip to content

Commit

Permalink
Merge pull request Expensify#43081 from rushatgabhane/view-trip-details
Browse files Browse the repository at this point in the history
[VIP][Travel] View trip details and authenticate to travelDot
  • Loading branch information
stitesExpensify authored Jun 17, 2024
2 parents 5957d9a + 9033b4e commit 32e5dd2
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3319,6 +3319,11 @@ const CONST = {

CONCIERGE_TRAVEL_URL: 'https://community.expensify.com/discussion/7066/introducing-concierge-travel',
BOOK_TRAVEL_DEMO_URL: 'https://calendly.com/d/ck2z-xsh-q97/expensify-travel-demo-travel-page',
TRAVEL_DOT_URL: 'https://travel.expensify.com',
STAGING_TRAVEL_DOT_URL: 'https://staging.travel.expensify.com',
TRIP_ID_PATH: (tripID: string) => `trips/${tripID}`,
SPOTNANA_TMC_ID: '8e8e7258-1cf3-48c0-9cd1-fe78a6e31eed',
STAGING_SPOTNANA_TMC_ID: '7a290c6e-5328-4107-aff6-e48765845b81',
SCREEN_READER_STATES: {
ALL: 'all',
ACTIVE: 'active',
Expand Down
17 changes: 16 additions & 1 deletion src/components/ReportActionItem/MoneyRequestView.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, {useCallback, useMemo} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import {useOnyx, withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import {useSession} from '@components/OnyxProvider';
Expand Down Expand Up @@ -34,6 +35,7 @@ import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import Navigation from '@navigation/Navigation';
import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
import * as IOU from '@userActions/IOU';
import * as Link from '@userActions/Link';
import * as Transaction from '@userActions/Transaction';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
Expand Down Expand Up @@ -99,6 +101,8 @@ function MoneyRequestView({
const session = useSession();
const {isOffline} = useNetwork();
const {translate, toLocaleDigit} = useLocalize();
const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);

const parentReportAction = parentReportActions?.[report.parentReportActionID ?? '-1'] ?? null;
const isTrackExpense = ReportUtils.isTrackExpenseReport(report);
const {canUseViolations, canUseP2PDistanceRequests} = usePermissions(isTrackExpense ? CONST.IOU.TYPE.TRACK : undefined);
Expand Down Expand Up @@ -169,6 +173,8 @@ function MoneyRequestView({
const shouldShowBillable = isPolicyExpenseChat && (!!transactionBillable || !(policy?.disabledFields?.defaultBillable ?? true));

const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest);
const tripID = ReportUtils.getTripIDFromTransactionParentReport(parentReport);
const shouldShowViewTripDetails = TransactionUtils.hasReservationList(transaction) && !!tripID;

const {getViolationsForField} = useViolations(transactionViolations ?? []);
const hasViolations = useCallback(
Expand Down Expand Up @@ -545,6 +551,15 @@ function MoneyRequestView({
/>
</OfflineWithFeedback>
)}
{shouldShowViewTripDetails && (
<MenuItem
title={translate('travel.viewTripDetails')}
icon={Expensicons.Suitcase}
iconRight={Expensicons.NewWindow}
shouldShowRightIcon
onPress={() => Link.openTravelDotLink(activePolicyID, CONST.TRIP_ID_PATH(tripID))}
/>
)}
{shouldShowBillable && (
<View style={[styles.flexRow, styles.optionRow, styles.justifyContentBetween, styles.alignItemsCenter, styles.ml5, styles.mr8]}>
<View>
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,7 @@ export default {
hotel: 'Hotel',
car: 'Car',
viewTrip: 'View trip',
viewTripDetails: 'View trip details',
trip: 'Trip',
tripSummary: 'Trip summary',
departs: 'Departs',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1952,6 +1952,7 @@ export default {
hotel: 'Hotel',
car: 'Auto',
viewTrip: 'Ver viaje',
viewTripDetails: 'Ver detalles del viaje',
trip: 'Viaje',
tripSummary: 'Resumen del viaje',
departs: 'Sale',
Expand Down
5 changes: 5 additions & 0 deletions src/libs/API/parameters/GenerateSpotnanaTokenParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type GenerateSpotnanaTokenParams = {
policyID: string;
};

export default GenerateSpotnanaTokenParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,5 @@ export type {default as PayInvoiceParams} from './PayInvoiceParams';
export type {default as MarkAsCashParams} from './MarkAsCashParams';
export type {default as UpdateSubscriptionTypeParams} from './UpdateSubscriptionTypeParams';
export type {default as SignUpUserParams} from './SignUpUserParams';
export type {default as GenerateSpotnanaTokenParams} from './GenerateSpotnanaTokenParams';
export type {default as UpdateSubscriptionSizeParams} from './UpdateSubscriptionSizeParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ const SIDE_EFFECT_REQUEST_COMMANDS = {
GET_MISSING_ONYX_MESSAGES: 'GetMissingOnyxMessages',
JOIN_POLICY_VIA_INVITE_LINK: 'JoinWorkspaceViaInviteLink',
RECONNECT_APP: 'ReconnectApp',
GENERATE_SPOTNANA_TOKEN: 'GenerateSpotnanaToken',
} as const;

type SideEffectRequestCommand = ValueOf<typeof SIDE_EFFECT_REQUEST_COMMANDS>;
Expand All @@ -566,6 +567,7 @@ type SideEffectRequestCommandParameters = {
[SIDE_EFFECT_REQUEST_COMMANDS.GET_MISSING_ONYX_MESSAGES]: Parameters.GetMissingOnyxMessagesParams;
[SIDE_EFFECT_REQUEST_COMMANDS.JOIN_POLICY_VIA_INVITE_LINK]: Parameters.JoinPolicyInviteLinkParams;
[SIDE_EFFECT_REQUEST_COMMANDS.RECONNECT_APP]: Parameters.ReconnectAppParams;
[SIDE_EFFECT_REQUEST_COMMANDS.GENERATE_SPOTNANA_TOKEN]: Parameters.GenerateSpotnanaTokenParams;
};

type ApiRequestCommandParameters = WriteCommandParameters & ReadCommandParameters & SideEffectRequestCommandParameters;
Expand Down
24 changes: 23 additions & 1 deletion src/libs/Environment/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ const OLDDOT_ENVIRONMENT_URLS = {
[CONST.ENVIRONMENT.ADHOC]: CONST.STAGING_EXPENSIFY_URL,
};

const TRAVELDOT_ENVIRONMENT_URLS: Record<string, string> = {
[CONST.ENVIRONMENT.DEV]: CONST.STAGING_TRAVEL_DOT_URL,
[CONST.ENVIRONMENT.STAGING]: CONST.STAGING_TRAVEL_DOT_URL,
[CONST.ENVIRONMENT.PRODUCTION]: CONST.TRAVEL_DOT_URL,
[CONST.ENVIRONMENT.ADHOC]: CONST.STAGING_TRAVEL_DOT_URL,
};

const SPOTNANA_ENVIRONMENT_TMC_ID: Record<string, string> = {
[CONST.ENVIRONMENT.DEV]: CONST.STAGING_SPOTNANA_TMC_ID,
[CONST.ENVIRONMENT.STAGING]: CONST.STAGING_SPOTNANA_TMC_ID,
[CONST.ENVIRONMENT.PRODUCTION]: CONST.SPOTNANA_TMC_ID,
[CONST.ENVIRONMENT.ADHOC]: CONST.STAGING_SPOTNANA_TMC_ID,
};

/**
* Are we running the app in development?
*/
Expand Down Expand Up @@ -54,4 +68,12 @@ function getOldDotEnvironmentURL(): Promise<string> {
return getEnvironment().then((environment) => OLDDOT_ENVIRONMENT_URLS[environment]);
}

export {getEnvironment, isInternalTestBuild, isDevelopment, isProduction, getEnvironmentURL, getOldDotEnvironmentURL};
function getTravelDotEnvironmentURL(): Promise<string> {
return getEnvironment().then((environment) => TRAVELDOT_ENVIRONMENT_URLS[environment]);
}

function getSpotnanaEnvironmentTMCID(): Promise<string> {
return getEnvironment().then((environment) => SPOTNANA_ENVIRONMENT_TMC_ID[environment]);
}

export {getEnvironment, isInternalTestBuild, isDevelopment, isProduction, getEnvironmentURL, getOldDotEnvironmentURL, getTravelDotEnvironmentURL, getSpotnanaEnvironmentTMCID};
5 changes: 5 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6779,6 +6779,10 @@ function getTripTransactions(tripRoomReportID: string | undefined): Transaction[
return tripTransactionReportIDs.flatMap((reportID) => TransactionUtils.getAllReportTransactions(reportID));
}

function getTripIDFromTransactionParentReport(transactionParentReport: OnyxEntry<Report> | undefined | null): string | undefined {
return getReport(transactionParentReport?.parentReportID)?.tripData?.tripID;
}

/**
* Checks if report contains actions with errors
*/
Expand Down Expand Up @@ -7224,6 +7228,7 @@ export {
updateReportPreview,
temporary_getMoneyRequestOptions,
getTripTransactions,
getTripIDFromTransactionParentReport,
buildOptimisticInvoiceReport,
getInvoiceChatByParticipants,
shouldShowMerchantColumn,
Expand Down
9 changes: 8 additions & 1 deletion src/libs/Url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ function addTrailingForwardSlash(url: string): string {
return url;
}

function addLeadingForwardSlash(url: string): string {
if (!url.startsWith('/')) {
return `/${url}`;
}
return url;
}

/**
* Get path from URL string
*/
Expand Down Expand Up @@ -63,4 +70,4 @@ function hasURL(text: string) {
return urlPattern.test(text);
}

export {addTrailingForwardSlash, hasSameExpensifyOrigin, getPathFromURL, appendParam, hasURL};
export {addTrailingForwardSlash, hasSameExpensifyOrigin, getPathFromURL, appendParam, hasURL, addLeadingForwardSlash};
38 changes: 37 additions & 1 deletion src/libs/actions/Link.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Onyx from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import * as API from '@libs/API';
import type {GenerateSpotnanaTokenParams} from '@libs/API/parameters';
import {SIDE_EFFECT_REQUEST_COMMANDS} from '@libs/API/types';
import asyncOpenURL from '@libs/asyncOpenURL';
import * as Environment from '@libs/Environment/Environment';
Expand Down Expand Up @@ -64,6 +66,40 @@ function openOldDotLink(url: string) {
);
}

function buildTravelDotURL(spotnanaToken?: string, postLoginPath?: string): Promise<string> {
return Promise.all([Environment.getTravelDotEnvironmentURL(), Environment.getSpotnanaEnvironmentTMCID()]).then(([environmentURL, tmcID]) => {
const authCode = spotnanaToken ? `authCode=${spotnanaToken}` : '';
const redirectURL = postLoginPath ? `redirectUrl=${Url.addLeadingForwardSlash(postLoginPath)}` : '';
const tmcIDParam = `tmcId=${tmcID}`;

const paramsArray = [authCode, tmcIDParam, redirectURL];
const params = paramsArray.filter(Boolean).join('&');
const travelDotDomain = Url.addTrailingForwardSlash(environmentURL);
return `${travelDotDomain}auth/code?${params}`;
});
}

/**
* @param postLoginPath When provided, we will redirect the user to this path post login on travelDot. eg: 'trips/:tripID'
*/
function openTravelDotLink(policyID: OnyxEntry<string>, postLoginPath?: string) {
if (policyID === null || policyID === undefined) {
return;
}

const parameters: GenerateSpotnanaTokenParams = {
policyID,
};

asyncOpenURL(
// eslint-disable-next-line rulesdir/no-api-side-effects-method
API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.GENERATE_SPOTNANA_TOKEN, parameters, {})
.then((response) => (response?.spotnanaToken ? buildTravelDotURL(response.spotnanaToken, postLoginPath) : buildTravelDotURL()))
.catch(() => buildTravelDotURL()),
(travelDotURL) => travelDotURL,
);
}

function getInternalNewExpensifyPath(href: string) {
const attrPath = Url.getPathFromURL(href);
return (Url.hasSameExpensifyOrigin(href, CONST.NEW_EXPENSIFY_URL) || Url.hasSameExpensifyOrigin(href, CONST.STAGING_NEW_EXPENSIFY_URL) || href.startsWith(CONST.DEV_NEW_EXPENSIFY_URL)) &&
Expand Down Expand Up @@ -121,4 +157,4 @@ function openLink(href: string, environmentURL: string, isAttachment = false) {
openExternalLink(href);
}

export {buildOldDotURL, openOldDotLink, openExternalLink, openLink, getInternalNewExpensifyPath, getInternalExpensifyPath};
export {buildOldDotURL, openOldDotLink, openExternalLink, openLink, getInternalNewExpensifyPath, getInternalExpensifyPath, openTravelDotLink};
12 changes: 12 additions & 0 deletions src/types/onyx/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,18 @@ type Report = OnyxCommon.OnyxValueWithOfflineFeedback<

/** Collection of report permissions granted to the current user */
permissions?: Array<ValueOf<typeof CONST.REPORT.PERMISSIONS>>;

/** The trip data for a trip room */
tripData?: {
/** The start date of a trip */
startDate: string;

/** The end date of a trip */
endDate: string;

/** The trip ID in spotnana */
tripID: string;
};
},
PolicyReportField['fieldID']
>;
Expand Down
3 changes: 3 additions & 0 deletions src/types/onyx/Response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ type Response = {
/** Short lived auth token generated by API */
shortLivedAuthToken?: string;

/** Short lived token generated by spotnana for authenticating travelDot */
spotnanaToken?: string;

/** User authorization token to authorize Pusher connections */
auth?: string;

Expand Down

0 comments on commit 32e5dd2

Please sign in to comment.