Skip to content

Commit

Permalink
Merge branch 'main' into fix-persisted-requests
Browse files Browse the repository at this point in the history
  • Loading branch information
shubham1206agra authored Oct 5, 2023
2 parents 05f36bf + 6d86d5a commit ae8cb00
Show file tree
Hide file tree
Showing 53 changed files with 582 additions and 269 deletions.
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001037703
versionName "1.3.77-3"
versionCode 1001037800
versionName "1.3.78-0"
}

flavorDimensions "default"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@
title: Lyft & Expensify
description: Setting up and using Expensify's Lyft integration
---
<!-- The lines above are required by Jekyll to process the .md file -->

# Overview
You can link Expensify directly to your Lyft account. This means that your receipts for work-related Lyft rides will populate automatically in Expensify.
You can link Expensify directly to your Lyft account. This means that your receipts for work-related Lyft rides will populate automatically in Expensify.

# How to connect a Lyft Business profile for individual use
If you use Lyft for work, you can connect your business profile to Expensify to have your Business-related trips populate directly in your Expensify account.
1. Open the Lyft mobile app and tap your profile photo in the upper left.
1. Open the Lyft mobile app and tap your profile photo in the upper left.
2. Go to Settings.
3. Select Business profile, and create one if you don’t already have one.
3. Select Business profile, and create one if you don’t already have one.
4. Tap Expense management and choose Expensify. You'll be asked to enter your email, make sure it matches the email you use to access Expensify.
You're all set! Switch into business profile mode each time you take a ride for work, and your receipts will automatically populate in Expensify.

You're all set! Switch into business profile mode each time you take a ride for work, and your receipts will automatically populate in Expensify.

# How to set up Lyft for Business
If you have a Lyft for Business account for your company, setting up the Expensify and Lyft integration for your whole team is very straightforward:
1. Open your Lyft for Business portal from a web browser.
2. Navigate to People > Business Profile (+) and input each team member's work email.
3. Once invited, each employee will receive an email prompting Lyft Business Profile setup.
3. Once invited, each employee will receive an email prompting Lyft Business Profile setup.
4. After they set up their profiles, employees can then switch into business profile mode each time they take a ride for work, and their receipts will automatically populate in Expensify.

Now, when any employee completes a ride on a Lyft Business Profile, it will show up in the "Rides" section of your Lyft for Business portal.

![Setting up the Lyft Integration: Connecting your account](https://help.expensify.com/assets/images/ExpensifyHelp_Lyft_01.png){:width="100%"}
Binary file added docs/assets/images/ExpensifyHelp_Lyft_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions ios/NewExpensify/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.3.77</string>
<string>1.3.78</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
Expand All @@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.3.77.3</string>
<string>1.3.78.0</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
4 changes: 2 additions & 2 deletions ios/NewExpensifyTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.3.77</string>
<string>1.3.78</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.3.77.3</string>
<string>1.3.78.0</string>
</dict>
</plist>
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "new.expensify",
"version": "1.3.77-3",
"version": "1.3.78-0",
"author": "Expensify, Inc.",
"homepage": "https://new.expensify.com",
"description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
1 change: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ const CONST = {
MAX_RETRY_WAIT_TIME_MS: 10 * 1000,
PROCESS_REQUEST_DELAY_MS: 1000,
MAX_PENDING_TIME_MS: 10 * 1000,
MAX_REQUEST_RETRIES: 10,
},
DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'},
DEFAULT_ACCOUNT_DATA: {errors: null, success: '', isLoading: false},
Expand Down
74 changes: 57 additions & 17 deletions src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, {useState, useCallback, useRef} from 'react';
import React, {useState, useCallback, useRef, useMemo} from 'react';
import PropTypes from 'prop-types';
import {View, Animated, Keyboard} from 'react-native';
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import lodashExtend from 'lodash/extend';
import _ from 'underscore';
import {withOnyx} from 'react-native-onyx';
import CONST from '../CONST';
import Modal from './Modal';
import AttachmentView from './Attachments/AttachmentView';
Expand All @@ -30,6 +31,10 @@ import useWindowDimensions from '../hooks/useWindowDimensions';
import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import useNativeDriver from '../libs/useNativeDriver';
import * as ReportUtils from '../libs/ReportUtils';
import * as ReportActionsUtils from '../libs/ReportActionsUtils';
import ONYXKEYS from '../ONYXKEYS';
import * as Policy from '../libs/actions/Policy';
import useNetwork from '../hooks/useNetwork';

/**
Expand Down Expand Up @@ -326,6 +331,37 @@ function AttachmentModal(props) {

const sourceForAttachmentView = props.source || source;

const threeDotsMenuItems = useMemo(() => {
if (!isAttachmentReceipt || !props.parentReport || !props.parentReportActions) {
return [];
}
const menuItems = [];
const parentReportAction = props.parentReportActions[props.report.parentReportActionID];
const isDeleted = ReportActionsUtils.isDeletedAction(parentReportAction);
const isSettled = ReportUtils.isSettled(props.parentReport.reportID);

const isAdmin = Policy.isAdminOfFreePolicy([props.policy]) && ReportUtils.isExpenseReport(props.parentReport);
const isRequestor = ReportUtils.isMoneyRequestReport(props.parentReport) && lodashGet(props.session, 'accountID', null) === parentReportAction.actorAccountID;
const canEdit = !isSettled && !isDeleted && (isAdmin || isRequestor);
if (canEdit) {
menuItems.push({
icon: Expensicons.Camera,
text: props.translate('common.replace'),
onSelected: () => {
onModalHideCallbackRef.current = () => Navigation.navigate(ROUTES.getEditRequestRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT));
closeModal();
},
});
}
menuItems.push({
icon: Expensicons.Download,
text: props.translate('common.download'),
onSelected: () => downloadAttachment(source),
});
return menuItems;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAttachmentReceipt, props.parentReport, props.parentReportActions, props.policy]);

return (
<>
<Modal
Expand Down Expand Up @@ -360,21 +396,7 @@ function AttachmentModal(props) {
onCloseButtonPress={closeModal}
shouldShowThreeDotsButton={isAttachmentReceipt}
threeDotsAnchorPosition={styles.threeDotsPopoverOffsetAttachmentModal(windowWidth)}
threeDotsMenuItems={[
{
icon: Expensicons.Camera,
text: props.translate('common.replace'),
onSelected: () => {
onModalHideCallbackRef.current = () => Navigation.navigate(ROUTES.EDIT_REQUEST.getRoute(props.report.reportID, CONST.EDIT_REQUEST_FIELD.RECEIPT));
closeModal();
},
},
{
icon: Expensicons.Download,
text: props.translate('common.download'),
onSelected: () => downloadAttachment(source),
},
]}
threeDotsMenuItems={threeDotsMenuItems}
shouldOverlay
/>
<View style={styles.imageModalImageCenterContainer}>
Expand Down Expand Up @@ -444,4 +466,22 @@ function AttachmentModal(props) {
AttachmentModal.propTypes = propTypes;
AttachmentModal.defaultProps = defaultProps;
AttachmentModal.displayName = 'AttachmentModal';
export default compose(withWindowDimensions, withLocalize)(AttachmentModal);
export default compose(
withWindowDimensions,
withLocalize,
withOnyx({
parentReport: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT}${report ? report.parentReportID : '0'}`,
},
policy: {
key: ({report}) => `${ONYXKEYS.COLLECTION.POLICY}${report ? report.policyID : '0'}`,
},
parentReportActions: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report ? report.parentReportID : '0'}`,
canEvict: false,
},
session: {
key: ONYXKEYS.SESSION,
},
}),
)(AttachmentModal);
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const pagePropTypes = {
/** Whether source url requires authentication */
isAuthTokenRequired: PropTypes.bool,

/** URL to full-sized attachment or SVG function */
/** URL to full-sized attachment, SVG function, or numeric static image on native platforms */
source: AttachmentsPropTypes.attachmentSourcePropType.isRequired,

isActive: PropTypes.bool.isRequired,
Expand Down
9 changes: 5 additions & 4 deletions src/components/Attachments/AttachmentView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ function AttachmentView({
);
}

// Check both source and file.name since PDFs dragged into the the text field
// Check both source and file.name since PDFs dragged into the text field
// will appear with a source that is a blob
if (Str.isPDF(source) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) {
if ((_.isString(source) && Str.isPDF(source)) || (file && Str.isPDF(file.name || translate('attachmentView.unknownFilename')))) {
const encryptedSourceUrl = isAuthTokenRequired ? addEncryptedAuthTokenToURL(source) : source;

return (
Expand All @@ -114,8 +114,9 @@ function AttachmentView({
}

// For this check we use both source and file.name since temporary file source is a blob
// both PDFs and images will appear as images when pasted into the the text field
const isImage = Str.isImage(source);
// both PDFs and images will appear as images when pasted into the text field.
// We also check for numeric source since this is how static images (used for preview) are represented in RN.
const isImage = typeof source === 'number' || Str.isImage(source);
if (isImage || (file && Str.isImage(file.name))) {
return (
<AttachmentViewImage
Expand Down
4 changes: 2 additions & 2 deletions src/components/Attachments/AttachmentView/propTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const attachmentViewPropTypes = {
/** Whether source url requires authentication */
isAuthTokenRequired: PropTypes.bool,

/** URL to full-sized attachment or SVG function */
/** URL to full-sized attachment, SVG function, or numeric static image on native platforms */
source: AttachmentsPropTypes.attachmentSourcePropType.isRequired,

/** File object maybe be instance of File or Object */
/** File object can be an instance of File or Object */
file: AttachmentsPropTypes.attachmentFilePropType,

/** Whether this view is the active screen */
Expand Down
6 changes: 3 additions & 3 deletions src/components/Attachments/propTypes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';

const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func]);
const attachmentSourcePropType = PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.number]);
const attachmentFilePropType = PropTypes.shape({
name: PropTypes.string,
});
Expand All @@ -9,10 +9,10 @@ const attachmentPropType = PropTypes.shape({
/** Whether source url requires authentication */
isAuthTokenRequired: PropTypes.bool,

/** URL to full-sized attachment or SVG function */
/** URL to full-sized attachment, SVG function, or numeric static image on native platforms */
source: attachmentSourcePropType.isRequired,

/** File object maybe be instance of File or Object */
/** File object can be an instance of File or Object */
file: attachmentFilePropType,
});

Expand Down
2 changes: 0 additions & 2 deletions src/components/HeaderWithBackButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ function HeaderWithBackButton({
threeDotsMenuItems = [],
shouldEnableDetailPageNavigation = false,
children = null,
onModalHide = () => {},
shouldOverlay = false,
}) {
const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState();
Expand Down Expand Up @@ -140,7 +139,6 @@ function HeaderWithBackButton({
menuItems={threeDotsMenuItems}
onIconPress={onThreeDotsButtonPress}
anchorPosition={threeDotsAnchorPosition}
onModalHide={onModalHide}
shouldOverlay={shouldOverlay}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _ from 'underscore';
import {SvgProps} from 'react-native-svg';
import * as Expensicons from './Expensicons';
import AmericanExpress from '../../../assets/images/bankicons/american-express.svg';
import BankOfAmerica from '../../../assets/images/bankicons/bank-of-america.svg';
Expand All @@ -21,14 +21,16 @@ import USBank from '../../../assets/images/bankicons/us-bank.svg';
import USAA from '../../../assets/images/bankicons/usaa.svg';
import variables from '../../styles/variables';

type BankIcon = {
icon: React.FC<SvgProps>;
iconSize?: number;
};

/**
* Returns matching asset icon for bankName
* @param {String} bankName
* @param {Boolean} isCard
* @returns {Object}
*/

function getAssetIcon(bankName, isCard) {
function getAssetIcon(bankName: string, isCard: boolean): React.FC<SvgProps> {
if (bankName.includes('americanexpress')) {
return AmericanExpress;
}
Expand Down Expand Up @@ -106,13 +108,10 @@ function getAssetIcon(bankName, isCard) {

/**
* Returns Bank Icon Object that matches to existing bank icons or default icons
* @param {String} bankName
* @param {Boolean} [isCard = false]
* @returns {Object} Object includes props icon, iconSize only if applicable
*/

export default function getBankIcon(bankName, isCard) {
const bankIcon = {
export default function getBankIcon(bankName: string, isCard = false): BankIcon {
const bankIcon: BankIcon = {
icon: isCard ? Expensicons.CreditCard : GenericBank,
};

Expand All @@ -121,7 +120,7 @@ export default function getBankIcon(bankName, isCard) {
}

// For default Credit Card icon the icon size should not be set.
if (!_.contains([Expensicons.CreditCard], bankIcon.icon)) {
if (![Expensicons.CreditCard].includes(bankIcon.icon)) {
bankIcon.iconSize = variables.iconSizeExtraLarge;
}

Expand Down
Loading

0 comments on commit ae8cb00

Please sign in to comment.