Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/Expensify/App into fix-regr…
Browse files Browse the repository at this point in the history
…ession-amount-setting-zero
  • Loading branch information
BhuvaneshPatil committed Dec 1, 2023
2 parents 93825e5 + c7eb0e9 commit 3317ca5
Show file tree
Hide file tree
Showing 21 changed files with 150 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,30 @@ description: Get the most out of your Expensify Card with exclusive perks!


# Overview
The Expensify Card is packed with perks, both native to our Card program and through exclusive discounts with partnering solutions. Below, we’ll cover all of our exclusive offers in more detail and how to claim discounts with our partners.
The Expensify Card is packed with perks, both native to our Card program and through exclusive discounts with partnering solutions. The Expensify Card’s primary perks include:
- Swipe to Win, where every swipe has a chance to win fun personalized gifts for you and your closest friends and family members
- Unbeatable cash back incentive with each swipe
Below, we’ll cover all of our exclusive offers in more detail and how to claim discounts with our partners.

# Expensify Card Perks

## Swipe to Win
Swipe to Win is a new [Expensify Card](https://use.expensify.com/company-credit-card) perk that gives cardholders the chance to send a gift to a friend, family member, or essential worker on the frontlines!

Winners can choose to _Send a Smile_ or _Send a Laugh_. To start, we’re offering one gift per option:

- **Send A Smile:** Champagne by Expensify
- **Send a Laugh:** Jenga Set

**How to Participate**
It’s easy! Once you have an Expensify Card, you just need to start using it. With each swipe, you're automatically entered to win and have a 1 in 250 chance of getting a prize!

**How will I know if I’ve won?**
Winners will be notified immediately via the Expensify app, and receive additional instructions on how to choose and send their desired gift.

If you don't have Expensify notifications turned on yet, here are some helpful guides:
- [Apple Notification Preferences](https://support.apple.com/en-us/HT201925)
- [Android Notification Preferences](https://community.expensify.com/home/leaving?allowTrusted=1&target=https%3A%2F%2Fsupport.google.com%2Fandroid%2Fanswer%2F9079661%3Fhl%3Den)

# Partner Specific Perks

Expand Down Expand Up @@ -188,4 +211,3 @@ Stripe Atlas helps removes obstacles typically associated with starting a busine
**Receive $100 off Stripe Atlas and get access to a startup toolkit and special offers on additional Strip Atlas services.**

**How to redeem:** Sign up with your Expensify Card.

4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ const CONST = {
MODIFIEDEXPENSE: 'MODIFIEDEXPENSE',
MOVED: 'MOVED',
REIMBURSEMENTQUEUED: 'REIMBURSEMENTQUEUED',
REIMBURSEMENTDEQUEUED: 'REIMBURSEMENTDEQUEUED',
RENAMED: 'RENAMED',
REPORTPREVIEW: 'REPORTPREVIEW',
SUBMITTED: 'SUBMITTED',
Expand Down Expand Up @@ -1165,6 +1166,9 @@ const CONST = {
SVG: 'svg',
},
RECEIPT_ERROR: 'receiptError',
CANCEL_REASON: {
PAYMENT_EXPIRED: 'CANCEL_REASON_PAYMENT_EXPIRED',
},
},

GROWL: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import * as UserUtils from '@libs/UserUtils';
Expand All @@ -9,38 +8,33 @@ import * as Expensicons from './Icon/Expensicons';
import Indicator from './Indicator';
import Tooltip from './Tooltip';

const propTypes = {
type AvatarWithIndicatorProps = {
/** URL for the avatar */
source: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
source: UserUtils.AvatarSource;

/** To show a tooltip on hover */
tooltipText: PropTypes.string,
tooltipText?: string;

/** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */
fallbackIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
fallbackIcon?: UserUtils.AvatarSource;

/** Indicates whether the avatar is loaded or not */
isLoading: PropTypes.bool,
isLoading?: boolean;
};

const defaultProps = {
tooltipText: '',
fallbackIcon: Expensicons.FallbackAvatar,
isLoading: true,
};

function AvatarWithIndicator(props) {
function AvatarWithIndicator({source, tooltipText = '', fallbackIcon = Expensicons.FallbackAvatar, isLoading = true}: AvatarWithIndicatorProps) {
const styles = useThemeStyles();

return (
<Tooltip text={props.tooltipText}>
<Tooltip text={tooltipText}>
<View style={[styles.sidebarAvatar]}>
{props.isLoading ? (
{isLoading ? (
<AvatarSkeleton />
) : (
<>
<Avatar
source={UserUtils.getSmallSizeAvatar(props.source)}
fallbackIcon={props.fallbackIcon}
source={UserUtils.getSmallSizeAvatar(source)}
fallbackIcon={fallbackIcon}
/>
<Indicator />
</>
Expand All @@ -50,8 +44,6 @@ function AvatarWithIndicator(props) {
);
}

AvatarWithIndicator.defaultProps = defaultProps;
AvatarWithIndicator.propTypes = propTypes;
AvatarWithIndicator.displayName = 'AvatarWithIndicator';

export default AvatarWithIndicator;
2 changes: 1 addition & 1 deletion src/components/Modal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type PopoverAnchorPosition = {
};

type BaseModalProps = WindowDimensionsProps &
ModalProps & {
Partial<ModalProps> & {
/** Decides whether the modal should cover fullscreen. FullScreen modal has backdrop */
fullscreen?: boolean;

Expand Down
4 changes: 3 additions & 1 deletion src/components/ReportActionItem/MoneyRequestPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ function MoneyRequestPreview(props) {
message += ` • ${props.translate('iou.approved')}`;
} else if (props.iouReport.isWaitingOnBankAccount) {
message += ` • ${props.translate('iou.pending')}`;
} else if (props.iouReport.isCancelledIOU) {
message += ` • ${props.translate('iou.canceled')}`;
}
return message;
};
Expand Down Expand Up @@ -280,7 +282,7 @@ function MoneyRequestPreview(props) {
<View style={styles.moneyRequestPreviewBoxText}>
<View style={[styles.flexRow]}>
<Text style={[styles.textLabelSupporting, styles.flex1, styles.lh20, styles.mb1]}>
{getPreviewHeaderText() + (isSettled ? ` • ${getSettledMessage()}` : '')}
{getPreviewHeaderText() + (isSettled && !props.iouReport.isCancelledIOU ? ` • ${getSettledMessage()}` : '')}
</Text>
{hasFieldErrors && (
<Icon
Expand Down
8 changes: 7 additions & 1 deletion src/components/ReportActionItem/MoneyRequestView.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ function MoneyRequestView({report, parentReport, policyCategories, shouldShowHor

// Flags for allowing or disallowing editing a money request
const isSettled = ReportUtils.isSettled(moneyRequestReport.reportID);
const isCancelled = moneyRequestReport && moneyRequestReport.isCancelledIOU;
const canEdit = ReportUtils.canEditMoneyRequest(parentReportAction);
const canEditAmount = canEdit && !isSettled && !isCardTransaction;

Expand All @@ -133,11 +134,16 @@ function MoneyRequestView({report, parentReport, policyCategories, shouldShowHor
if (TransactionUtils.isPending(transaction)) {
amountDescription += ` • ${translate('iou.pending')}`;
}
if (isCancelled) {
amountDescription += ` • ${translate('iou.canceled')}`;
}
} else {
if (!isDistanceRequest) {
amountDescription += ` • ${translate('iou.cash')}`;
}
if (isSettled) {
if (isCancelled) {
amountDescription += ` • ${translate('iou.canceled')}`;
} else if (isSettled) {
amountDescription += ` • ${translate('iou.settledExpensify')}`;
} else if (report.isWaitingOnBankAccount) {
amountDescription += ` • ${translate('iou.pending')}`;
Expand Down
48 changes: 20 additions & 28 deletions src/components/TestToolMenu.js → src/components/TestToolMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React from 'react';
import {withOnyx} from 'react-native-onyx';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import * as ApiUtils from '@libs/ApiUtils';
import compose from '@libs/compose';
import useThemeStyles from '@styles/useThemeStyles';
Expand All @@ -10,34 +8,30 @@ import * as Session from '@userActions/Session';
import * as User from '@userActions/User';
import CONFIG from '@src/CONFIG';
import ONYXKEYS from '@src/ONYXKEYS';
import NetworkOnyx from '@src/types/onyx/Network';
import UserOnyx from '@src/types/onyx/User';
import Button from './Button';
import networkPropTypes from './networkPropTypes';
import {withNetwork} from './OnyxProvider';
import Switch from './Switch';
import TestToolRow from './TestToolRow';
import Text from './Text';

const propTypes = {
type TestToolMenuOnyxProps = {
/** User object in Onyx */
user: PropTypes.shape({
/** Whether we should use the staging version of the secure API server */
shouldUseStagingServer: PropTypes.bool,
}),
user: OnyxEntry<UserOnyx>;
};

type TestToolMenuProps = TestToolMenuOnyxProps & {
/** Network object in Onyx */
network: networkPropTypes.isRequired,
network: OnyxEntry<NetworkOnyx>;
};

const defaultProps = {
user: {
// The default value is environment specific and can't be set with `defaultProps` (ENV is not resolved yet)
// When undefined (during render) STAGING defaults to `true`, other envs default to `false`
shouldUseStagingServer: undefined,
},
};
const USER_DEFAULT: UserOnyx = {shouldUseStagingServer: undefined, isSubscribedToNewsletter: false, validated: false, isFromPublicDomain: false, isUsingExpensifyCard: false};

function TestToolMenu(props) {
function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {
const shouldUseStagingServer = user?.shouldUseStagingServer ?? ApiUtils.isUsingStagingApi();
const styles = useThemeStyles();

return (
<>
<Text
Expand All @@ -54,8 +48,8 @@ function TestToolMenu(props) {
<TestToolRow title="Use Staging Server">
<Switch
accessibilityLabel="Use Staging Server"
isOn={lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi())}
onToggle={() => User.setShouldUseStagingServer(!lodashGet(props, 'user.shouldUseStagingServer', ApiUtils.isUsingStagingApi()))}
isOn={shouldUseStagingServer}
onToggle={() => User.setShouldUseStagingServer(!shouldUseStagingServer)}
/>
</TestToolRow>
)}
Expand All @@ -64,17 +58,17 @@ function TestToolMenu(props) {
<TestToolRow title="Force offline">
<Switch
accessibilityLabel="Force offline"
isOn={Boolean(props.network.shouldForceOffline)}
onToggle={() => Network.setShouldForceOffline(!props.network.shouldForceOffline)}
isOn={!!network?.shouldForceOffline}
onToggle={() => Network.setShouldForceOffline(!network?.shouldForceOffline)}
/>
</TestToolRow>

{/* When toggled all network requests will fail. */}
<TestToolRow title="Simulate failing network requests">
<Switch
accessibilityLabel="Simulate failing network requests"
isOn={Boolean(props.network.shouldFailAllRequests)}
onToggle={() => Network.setShouldFailAllRequests(!props.network.shouldFailAllRequests)}
isOn={!!network?.shouldFailAllRequests}
onToggle={() => Network.setShouldFailAllRequests(!network?.shouldFailAllRequests)}
/>
</TestToolRow>

Expand All @@ -99,15 +93,13 @@ function TestToolMenu(props) {
);
}

TestToolMenu.propTypes = propTypes;
TestToolMenu.defaultProps = defaultProps;
TestToolMenu.displayName = 'TestToolMenu';

export default compose(
withNetwork(),
withOnyx({
withOnyx<TestToolMenuProps, TestToolMenuOnyxProps>({
user: {
key: ONYXKEYS.USER,
},
}),
withNetwork(),
)(TestToolMenu);
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
import PropTypes from 'prop-types';
import React from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import {OnyxEntry, withOnyx} from 'react-native-onyx';
import useThemeStyles from '@styles/useThemeStyles';
import toggleTestToolsModal from '@userActions/TestTool';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import Modal from './Modal';
import TestToolMenu from './TestToolMenu';

const propTypes = {
/** Details about modal */
modal: PropTypes.shape({
/** Indicates when an Alert modal is about to be visible */
willAlertModalBecomeVisible: PropTypes.bool,
}),

type TestToolsModalOnyxProps = {
/** Whether the test tools modal is open */
isTestToolsModalOpen: PropTypes.bool,
isTestToolsModalOpen: OnyxEntry<boolean>;
};

const defaultProps = {
modal: {},
isTestToolsModalOpen: false,
};
type TestToolsModalProps = TestToolsModalOnyxProps;

function TestToolsModal(props) {
function TestToolsModal({isTestToolsModalOpen = false}: TestToolsModalProps) {
const styles = useThemeStyles();

return (
<Modal
isVisible={props.isTestToolsModalOpen}
isVisible={!!isTestToolsModalOpen}
type={CONST.MODAL.MODAL_TYPE.CENTERED_SMALL}
onClose={toggleTestToolsModal}
>
Expand All @@ -40,14 +31,9 @@ function TestToolsModal(props) {
);
}

TestToolsModal.propTypes = propTypes;
TestToolsModal.defaultProps = defaultProps;
TestToolsModal.displayName = 'TestToolsModal';

export default withOnyx({
modal: {
key: ONYXKEYS.MODAL,
},
export default withOnyx<TestToolsModalProps, TestToolsModalOnyxProps>({
isTestToolsModalOpen: {
key: ONYXKEYS.IS_TEST_TOOLS_MODAL_OPEN,
},
Expand Down
4 changes: 4 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
BeginningOfChatHistoryAnnounceRoomPartOneParams,
BeginningOfChatHistoryAnnounceRoomPartTwo,
BeginningOfChatHistoryDomainRoomPartOneParams,
CanceledRequestParams,
CharacterLimitParams,
ConfirmThatParams,
DateShouldBeAfterParams,
Expand Down Expand Up @@ -542,6 +543,7 @@ export default {
pay: 'Pay',
viewDetails: 'View details',
pending: 'Pending',
canceled: 'Canceled',
posted: 'Posted',
deleteReceipt: 'Delete receipt',
receiptScanning: 'Receipt scan in progress…',
Expand Down Expand Up @@ -572,6 +574,8 @@ export default {
managerApproved: ({manager}: ManagerApprovedParams) => `${manager} approved:`,
payerSettled: ({amount}: PayerSettledParams) => `paid ${amount}`,
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `started settling up, payment is held until ${submitterDisplayName} adds a bank account`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
`Canceled the ${amount} payment, because ${submitterDisplayName} did not enable their Expensify Wallet within 30 days`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
`${submitterDisplayName} added a bank account. The ${amount} payment has been made.`,
paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer} paid ${amount} elsewhere`,
Expand Down
4 changes: 4 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
BeginningOfChatHistoryAnnounceRoomPartOneParams,
BeginningOfChatHistoryAnnounceRoomPartTwo,
BeginningOfChatHistoryDomainRoomPartOneParams,
CanceledRequestParams,
CharacterLimitParams,
ConfirmThatParams,
DateShouldBeAfterParams,
Expand Down Expand Up @@ -534,6 +535,7 @@ export default {
pay: 'Pagar',
viewDetails: 'Ver detalles',
pending: 'Pendiente',
canceled: 'Canceló',
posted: 'Contabilizado',
deleteReceipt: 'Eliminar recibo',
receiptScanning: 'Escaneo de recibo en curso…',
Expand Down Expand Up @@ -564,6 +566,8 @@ export default {
managerApproved: ({manager}: ManagerApprovedParams) => `${manager} aprobó:`,
payerSettled: ({amount}: PayerSettledParams) => `pagó ${amount}`,
waitingOnBankAccount: ({submitterDisplayName}: WaitingOnBankAccountParams) => `inicio el pago, pero no se procesará hasta que ${submitterDisplayName} añada una cuenta bancaria`,
canceledRequest: ({amount, submitterDisplayName}: CanceledRequestParams) =>
`Canceló el pago ${amount}, porque ${submitterDisplayName} no habilitó su billetera Expensify en un plazo de 30 días.`,
settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) =>
`${submitterDisplayName} añadió una cuenta bancaria. El pago de ${amount} se ha realizado.`,
paidElsewhereWithAmount: ({payer, amount}: PaidElsewhereWithAmountParams) => `${payer} pagó ${amount} de otra forma`,
Expand Down
Loading

0 comments on commit 3317ca5

Please sign in to comment.