Skip to content

Commit

Permalink
Merge branch 'Expensify:main' into 37485-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
dragnoir authored Mar 21, 2024
2 parents 093c223 + 1a8b66a commit 6abc025
Show file tree
Hide file tree
Showing 79 changed files with 3,548 additions and 607 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/platformDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_PRODUCTION }}


- name: Build staging desktop app
if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
Expand All @@ -168,6 +170,7 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_STAGING }}

iOS:
name: Build and deploy iOS
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/testBuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ jobs:
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GCP_GEOLOCATION_API_KEY: $${{ secrets.GCP_GEOLOCATION_API_KEY_STAGING }}

web:
name: Build and deploy Web
Expand Down
5 changes: 5 additions & 0 deletions assets/images/document-plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion desktop/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const {DESKTOP_SHORTCUT_ACCELERATOR, LOCALES} = CONST;
// Setup google api key in process environment, we are setting it this way intentionally. It is required by the
// geolocation api (window.navigator.geolocation.getCurrentPosition) to work on desktop.
// Source: https://github.com/electron/electron/blob/98cd16d336f512406eee3565be1cead86514db7b/docs/api/environment-variables.md#google_api_key
process.env.GOOGLE_API_KEY = CONFIG.GOOGLE_GEOLOCATION_API_KEY;
process.env.GOOGLE_API_KEY = CONFIG.GCP_GEOLOCATION_API_KEY;

app.setName('New Expensify');

Expand Down
4 changes: 2 additions & 2 deletions src/CONFIG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const secureExpensifyUrl = Url.addTrailingForwardSlash(get(Config, 'SECURE_EXPEN
const useNgrok = get(Config, 'USE_NGROK', 'false') === 'true';
const useWebProxy = get(Config, 'USE_WEB_PROXY', 'true') === 'true';
const expensifyComWithProxy = getPlatform() === 'web' && useWebProxy ? '/' : expensifyURL;
const googleGeolocationAPIKey = get(Config, 'GOOGLE_GEOLOCATION_API_KEY', 'AIzaSyBqg6bMvQU7cPWDKhhzpYqJrTEnSorpiLI');
const googleGeolocationAPIKey = get(Config, 'GCP_GEOLOCATION_API_KEY', '');

// Throw errors on dev if config variables are not set correctly
if (ENVIRONMENT === CONST.ENVIRONMENT.DEV) {
Expand Down Expand Up @@ -94,5 +94,5 @@ export default {
WEB_CLIENT_ID: '921154746561-gpsoaqgqfuqrfsjdf8l7vohfkfj7b9up.apps.googleusercontent.com',
IOS_CLIENT_ID: '921154746561-s3uqn2oe4m85tufi6mqflbfbuajrm2i3.apps.googleusercontent.com',
},
GOOGLE_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
GCP_GEOLOCATION_API_KEY: googleGeolocationAPIKey,
} as const;
6 changes: 6 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ const CONST = {
BETA_COMMENT_LINKING: 'commentLinking',
VIOLATIONS: 'violations',
REPORT_FIELDS: 'reportFields',
TRACK_EXPENSE: 'trackExpense',
P2P_DISTANCE_REQUESTS: 'p2pDistanceRequests',
WORKFLOWS_DELAYED_SUBMISSION: 'workflowsDelayedSubmission',
},
Expand Down Expand Up @@ -1344,6 +1345,7 @@ const CONST = {
SEND: 'send',
SPLIT: 'split',
REQUEST: 'request',
TRACK_EXPENSE: 'track-expense',
},
REQUEST_TYPE: {
DISTANCE: 'distance',
Expand All @@ -1358,6 +1360,7 @@ const CONST = {
CANCEL: 'cancel',
DELETE: 'delete',
APPROVE: 'approve',
TRACK: 'track',
},
AMOUNT_MAX_LENGTH: 10,
RECEIPT_STATE: {
Expand Down Expand Up @@ -3414,6 +3417,9 @@ const CONST = {

REPORT_FIELD_TITLE_FIELD_ID: 'text_title',

MOBILE_PAGINATION_SIZE: 15,
WEB_PAGINATION_SIZE: 50,

/** Dimensions for illustration shown in Confirmation Modal */
CONFIRM_CONTENT_SVG_SIZE: {
HEIGHT: 220,
Expand Down
2 changes: 0 additions & 2 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ const ONYXKEYS = {
POLICY_TAGS: 'policyTags_',
POLICY_RECENTLY_USED_TAGS: 'nvp_recentlyUsedTags_',
OLD_POLICY_RECENTLY_USED_TAGS: 'policyRecentlyUsedTags_',
POLICY_REPORT_FIELDS: 'policyReportFields_',
WORKSPACE_INVITE_MEMBERS_DRAFT: 'workspaceInviteMembersDraft_',
WORKSPACE_INVITE_MESSAGE_DRAFT: 'workspaceInviteMessageDraft_',
REPORT: 'report_',
Expand Down Expand Up @@ -500,7 +499,6 @@ type OnyxCollectionValuesMapping = {
[ONYXKEYS.COLLECTION.POLICY_MEMBERS]: OnyxTypes.PolicyMembers;
[ONYXKEYS.COLLECTION.POLICY_MEMBERS_DRAFTS]: OnyxTypes.PolicyMember;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_CATEGORIES]: OnyxTypes.RecentlyUsedCategories;
[ONYXKEYS.COLLECTION.POLICY_REPORT_FIELDS]: OnyxTypes.PolicyReportFields;
[ONYXKEYS.COLLECTION.DEPRECATED_POLICY_MEMBER_LIST]: OnyxTypes.PolicyMembers;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT]: OnyxTypes.InvitedEmailsToAccountIDs;
[ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MESSAGE_DRAFT]: string;
Expand Down
12 changes: 6 additions & 6 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,9 @@ const ROUTES = {
getUrlWithBackToParam(`create/${iouType}/taxAmount/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_CATEGORY: {
route: ':action/:iouType/category/:transactionID/:reportID',
getRoute: (action: ValueOf<typeof CONST.IOU.ACTION>, iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string, backTo = '') =>
getUrlWithBackToParam(`${action}/${iouType}/category/${transactionID}/${reportID}`, backTo),
route: ':action/:iouType/category/:transactionID/:reportID/:reportActionID?',
getRoute: (action: ValueOf<typeof CONST.IOU.ACTION>, iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
getUrlWithBackToParam(`${action}/${iouType}/category/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_CURRENCY: {
route: 'create/:iouType/currency/:transactionID/:reportID/:pageIndex?',
Expand All @@ -347,9 +347,9 @@ const ROUTES = {
getUrlWithBackToParam(`${action}/${iouType}/date/${transactionID}/${reportID}`, backTo),
},
MONEY_REQUEST_STEP_DESCRIPTION: {
route: ':action/:iouType/description/:transactionID/:reportID',
getRoute: (action: ValueOf<typeof CONST.IOU.ACTION>, iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string, backTo = '') =>
getUrlWithBackToParam(`${action}/${iouType}/description/${transactionID}/${reportID}`, backTo),
route: ':action/:iouType/description/:transactionID/:reportID/:reportActionID?',
getRoute: (action: ValueOf<typeof CONST.IOU.ACTION>, iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string, backTo = '', reportActionID?: string) =>
getUrlWithBackToParam(`${action}/${iouType}/description/${transactionID}/${reportID}${reportActionID ? `/${reportActionID}` : ''}`, backTo),
},
MONEY_REQUEST_STEP_DISTANCE: {
route: 'create/:iouType/distance/:transactionID/:reportID',
Expand Down
2 changes: 1 addition & 1 deletion src/components/AvatarWithDisplayName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function AvatarWithDisplayName({
const title = ReportUtils.getReportName(report);
const subtitle = ReportUtils.getChatRoomSubtitle(report);
const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(report);
const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(report) || ReportUtils.isMoneyRequest(report);
const isMoneyRequestOrReport = ReportUtils.isMoneyRequestReport(report) || ReportUtils.isMoneyRequest(report) || ReportUtils.isTrackExpenseReport(report);
const icons = ReportUtils.getIcons(report, personalDetails, null, '', -1, policy);
const ownerPersonalDetails = OptionsListUtils.getPersonalDetailsForAccountIDs(report?.ownerAccountID ? [report.ownerAccountID] : [], personalDetails);
const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips(Object.values(ownerPersonalDetails) as PersonalDetails[], false);
Expand Down
16 changes: 8 additions & 8 deletions src/components/FlatList/MVCPFlatList.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizont
if (scrollRef.current == null) {
return 0;
}
return horizontal ? scrollRef.current.getScrollableNode().scrollLeft : scrollRef.current.getScrollableNode().scrollTop;
return horizontal ? scrollRef.current?.getScrollableNode()?.scrollLeft : scrollRef.current?.getScrollableNode()?.scrollTop;
}, [horizontal]);

const getContentView = React.useCallback(() => scrollRef.current?.getScrollableNode().childNodes[0], []);
const getContentView = React.useCallback(() => scrollRef.current?.getScrollableNode()?.childNodes[0], []);

const scrollToOffset = React.useCallback(
(offset, animated) => {
const behavior = animated ? 'smooth' : 'instant';
scrollRef.current?.getScrollableNode().scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior});
scrollRef.current?.getScrollableNode()?.scroll(horizontal ? {left: offset, behavior} : {top: offset, behavior});
},
[horizontal],
);
Expand All @@ -68,12 +68,13 @@ const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizont
}

const scrollOffset = getScrollOffset();
lastScrollOffsetRef.current = scrollOffset;

const contentViewLength = contentView.childNodes.length;
for (let i = mvcpMinIndexForVisible; i < contentViewLength; i++) {
const subview = contentView.childNodes[i];
const subviewOffset = horizontal ? subview.offsetLeft : subview.offsetTop;
if (subviewOffset > scrollOffset || i === contentViewLength - 1) {
if (subviewOffset > scrollOffset) {
prevFirstVisibleOffsetRef.current = subviewOffset;
firstVisibleViewRef.current = subview;
break;
Expand Down Expand Up @@ -126,6 +127,7 @@ const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizont
}

adjustForMaintainVisibleContentPosition();
prepareForMaintainVisibleContentPosition();
});
});
mutationObserver.observe(contentView, {
Expand All @@ -135,7 +137,7 @@ const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizont
});

mutationObserverRef.current = mutationObserver;
}, [adjustForMaintainVisibleContentPosition, getContentView, getScrollOffset, scrollToOffset]);
}, [adjustForMaintainVisibleContentPosition, prepareForMaintainVisibleContentPosition, getContentView, getScrollOffset, scrollToOffset]);

React.useEffect(() => {
if (!isListRenderedRef.current) {
Expand Down Expand Up @@ -172,13 +174,11 @@ const MVCPFlatList = React.forwardRef(({maintainVisibleContentPosition, horizont

const onScrollInternal = React.useCallback(
(ev) => {
lastScrollOffsetRef.current = getScrollOffset();

prepareForMaintainVisibleContentPosition();

onScroll?.(ev);
},
[getScrollOffset, prepareForMaintainVisibleContentPosition, onScroll],
[prepareForMaintainVisibleContentPosition, onScroll],
);

return (
Expand Down
7 changes: 1 addition & 6 deletions src/components/HoldMenuSectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,7 @@ function HoldMenuSectionList() {
/>
<View style={[styles.flex1, styles.justifyContentCenter]}>
<Text style={[styles.textStrong, styles.mb1]}>{translate(section.titleTranslationKey)}</Text>
<Text
style={[styles.textNormal]}
numberOfLines={3}
>
{translate(section.descriptionTranslationKey)}
</Text>
<Text style={[styles.textNormal]}>{translate(section.descriptionTranslationKey)}</Text>
</View>
</View>
))}
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Expensicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import Concierge from '@assets/images/concierge.svg';
import Connect from '@assets/images/connect.svg';
import Copy from '@assets/images/copy.svg';
import CreditCard from '@assets/images/creditcard.svg';
import DocumentPlus from '@assets/images/document-plus.svg';
import DocumentSlash from '@assets/images/document-slash.svg';
import Document from '@assets/images/document.svg';
import DotIndicatorUnfilled from '@assets/images/dot-indicator-unfilled.svg';
Expand Down Expand Up @@ -314,4 +315,5 @@ export {
ChatBubbleUnread,
ChatBubbleReply,
Lightbulb,
DocumentPlus,
};
34 changes: 24 additions & 10 deletions src/components/InvertedFlatList/BaseInvertedFlatList.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import type {ForwardedRef} from 'react';
import React, {forwardRef} from 'react';
import type {FlatListProps} from 'react-native';
import React, {forwardRef, useMemo} from 'react';
import type {FlatListProps, ScrollViewProps} from 'react-native';
import FlatList from '@components/FlatList';

const WINDOW_SIZE = 15;
type BaseInvertedFlatListProps<T> = FlatListProps<T> & {
shouldEnableAutoScrollToTopThreshold?: boolean;
};

const AUTOSCROLL_TO_TOP_THRESHOLD = 128;

const maintainVisibleContentPosition = {
minIndexForVisible: 0,
autoscrollToTopThreshold: AUTOSCROLL_TO_TOP_THRESHOLD,
};
function BaseInvertedFlatList<T>(props: BaseInvertedFlatListProps<T>, ref: ForwardedRef<FlatList>) {
const {shouldEnableAutoScrollToTopThreshold, ...rest} = props;

const maintainVisibleContentPosition = useMemo(() => {
const config: ScrollViewProps['maintainVisibleContentPosition'] = {
// This needs to be 1 to avoid using loading views as anchors.
minIndexForVisible: 1,
};

if (shouldEnableAutoScrollToTopThreshold) {
config.autoscrollToTopThreshold = AUTOSCROLL_TO_TOP_THRESHOLD;
}

return config;
}, [shouldEnableAutoScrollToTopThreshold]);

function BaseInvertedFlatList<T>(props: FlatListProps<T>, ref: ForwardedRef<FlatList>) {
return (
<FlatList
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
{...rest}
ref={ref}
windowSize={WINDOW_SIZE}
maintainVisibleContentPosition={maintainVisibleContentPosition}
inverted
/>
Expand All @@ -27,3 +39,5 @@ function BaseInvertedFlatList<T>(props: FlatListProps<T>, ref: ForwardedRef<Flat
BaseInvertedFlatList.displayName = 'BaseInvertedFlatList';

export default forwardRef(BaseInvertedFlatList);

export {AUTOSCROLL_TO_TOP_THRESHOLD};
6 changes: 5 additions & 1 deletion src/components/InvertedFlatList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import CONST from '@src/CONST';
import BaseInvertedFlatList from './BaseInvertedFlatList';
import CellRendererComponent from './CellRendererComponent';

type InvertedFlatListProps<T> = FlatListProps<T> & {
shouldEnableAutoScrollToTopThreshold?: boolean;
};

// This is adapted from https://codesandbox.io/s/react-native-dsyse
// It's a HACK alert since FlatList has inverted scrolling on web
function InvertedFlatList<T>({onScroll: onScrollProp = () => {}, ...props}: FlatListProps<T>, ref: ForwardedRef<FlatList>) {
function InvertedFlatList<T>({onScroll: onScrollProp = () => {}, ...props}: InvertedFlatListProps<T>, ref: ForwardedRef<FlatList>) {
const lastScrollEvent = useRef<number | null>(null);
const scrollEndTimeout = useRef<NodeJS.Timeout | null>(null);
const updateInProgress = useRef<boolean>(false);
Expand Down
10 changes: 9 additions & 1 deletion src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,14 @@ function MoneyRequestConfirmationList({
description={translate('common.description')}
onPress={() => {
Navigation.navigate(
ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction?.transactionID ?? '', reportID, Navigation.getActiveRouteWithoutParams()),
ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.getRoute(
CONST.IOU.ACTION.EDIT,
iouType,
transaction?.transactionID ?? '',
reportID,
Navigation.getActiveRouteWithoutParams(),
reportActionID,
),
);
}}
style={styles.moneyRequestMenuItem}
Expand Down Expand Up @@ -757,6 +764,7 @@ function MoneyRequestConfirmationList({
transaction?.transactionID ?? '',
reportID,
Navigation.getActiveRouteWithoutParams(),
reportActionID,
),
);
}}
Expand Down
11 changes: 8 additions & 3 deletions src/components/MoneyRequestHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
const deleteTransaction = useCallback(() => {
if (parentReportAction) {
const iouTransactionID = parentReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction.originalMessage?.IOUTransactionID ?? '' : '';
if (ReportActionsUtils.isTrackExpenseAction(parentReportAction)) {
IOU.deleteTrackExpense(parentReport?.reportID ?? '', iouTransactionID, parentReportAction, true);
return;
}
IOU.deleteMoneyRequest(iouTransactionID, parentReportAction, true);
}

setIsDeleteModalVisible(false);
}, [parentReportAction, setIsDeleteModalVisible]);
}, [parentReport?.reportID, parentReportAction, setIsDeleteModalVisible]);

const isScanning = TransactionUtils.hasReceipt(transaction) && TransactionUtils.isReceiptBeingScanned(transaction);
const isPending = TransactionUtils.isExpensifyCardTransaction(transaction) && TransactionUtils.isPending(transaction);
Expand All @@ -84,7 +88,7 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
const canHoldOrUnholdRequest = !isSettled && !isApproved && !isDeletedParentAction;

// If the report supports adding transactions to it, then it also supports deleting transactions from it.
const canDeleteRequest = isActionOwner && ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) && !isDeletedParentAction;
const canDeleteRequest = isActionOwner && (ReportUtils.canAddOrDeleteTransactions(moneyRequestReport) || ReportUtils.isTrackExpenseReport(report)) && !isDeletedParentAction;

const changeMoneyRequestStatus = () => {
const iouTransactionID = parentReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.IOU ? parentReportAction.originalMessage?.IOUTransactionID ?? '' : '';
Expand All @@ -109,7 +113,8 @@ function MoneyRequestHeader({session, parentReport, report, parentReportAction,
if (canHoldOrUnholdRequest) {
const isRequestIOU = parentReport?.type === 'iou';
const isHoldCreator = ReportUtils.isHoldCreator(transaction, report?.reportID) && isRequestIOU;
const canModifyStatus = isPolicyAdmin || isActionOwner || isApprover;
const isTrackExpenseReport = ReportUtils.isTrackExpenseReport(report);
const canModifyStatus = !isTrackExpenseReport && (isPolicyAdmin || isActionOwner || isApprover);
if (isOnHold && (isHoldCreator || (!isRequestIOU && canModifyStatus))) {
threeDotsMenuItems.push({
icon: Expensicons.Stopwatch,
Expand Down
Loading

0 comments on commit 6abc025

Please sign in to comment.