Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
dukenv0307 committed Apr 1, 2024
2 parents 744f3fb + 6fbce73 commit 8db0d5c
Show file tree
Hide file tree
Showing 46 changed files with 323 additions and 899 deletions.
2 changes: 1 addition & 1 deletion contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ C+ are contributors who are experienced at working with Expensify and have gaine

## How to join?

Email contributors@expensify.com and include "C+ Team Application" in the subject line if you’re interested in joining.
Email contributors@expensify.com and include "C+ Team Application" in the subject line if you’re interested in joining. Please include your GitHub username and a link to the PRs you've authored that have been merged. ie. `https://github.com/Expensify/App/pulls?q=is%3Apr+author%3Aparasharrajat+is%3Amerged`
6 changes: 3 additions & 3 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
Expand Up @@ -101,7 +101,7 @@
"date-fns-tz": "^2.0.0",
"dom-serializer": "^0.2.2",
"domhandler": "^4.3.0",
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#f7efbd084536c140e65b49cd15f67ad8a2a10675",
"expensify-common": "git+ssh://git@github.com/Expensify/expensify-common.git#1247a822328011083a13abc769ba1911c3586338",
"expo": "^50.0.3",
"expo-av": "~13.10.4",
"expo-image": "1.11.0",
Expand Down
3 changes: 3 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ const cardActiveStates: number[] = [2, 3, 4, 7];
const CONST = {
MERGED_ACCOUNT_PREFIX: 'MERGED_',
DEFAULT_POLICY_ROOM_CHAT_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL],

// Note: Group and Self-DM excluded as these are not tied to a Workspace
WORKSPACE_ROOM_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL, chatTypes.POLICY_ROOM, chatTypes.POLICY_EXPENSE_CHAT],
ANDROID_PACKAGE_NAME,
ANIMATED_TRANSITION: 300,
ANIMATED_TRANSITION_FROM_VALUE: 100,
Expand Down
9 changes: 3 additions & 6 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,6 @@ const ROUTES = {
route: ':iouType/new/participants/:reportID?',
getRoute: (iouType: string, reportID = '') => `${iouType}/new/participants/${reportID}` as const,
},
MONEY_REQUEST_CONFIRMATION: {
route: ':iouType/new/confirmation/:reportID?',
getRoute: (iouType: string, reportID = '') => `${iouType}/new/confirmation/${reportID}` as const,
},
MONEY_REQUEST_CURRENCY: {
route: ':iouType/new/currency/:reportID?',
getRoute: (iouType: string, reportID: string, currency: string, backTo: string) => `${iouType}/new/currency/${reportID}?currency=${currency}&backTo=${backTo}` as const,
Expand All @@ -307,8 +303,9 @@ const ROUTES = {
`${action}/${iouType}/start/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_CONFIRMATION: {
route: 'create/:iouType/confirmation/:transactionID/:reportID',
getRoute: (iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string) => `create/${iouType}/confirmation/${transactionID}/${reportID}` as const,
route: ':action/:iouType/confirmation/:transactionID/:reportID',
getRoute: (action: ValueOf<typeof CONST.IOU.ACTION>, iouType: ValueOf<typeof CONST.IOU.TYPE>, transactionID: string, reportID: string) =>
`${action}/${iouType}/confirmation/${transactionID}/${reportID}` as const,
},
MONEY_REQUEST_STEP_AMOUNT: {
route: ':action/:iouType/amount/:transactionID/:reportID',
Expand Down
1 change: 0 additions & 1 deletion src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ const SCREENS = {
STEP_TAX_AMOUNT: 'Money_Request_Step_Tax_Amount',
STEP_TAX_RATE: 'Money_Request_Step_Tax_Rate',
PARTICIPANTS: 'Money_Request_Participants',
CONFIRMATION: 'Money_Request_Confirmation',
CURRENCY: 'Money_Request_Currency',
WAYPOINT: 'Money_Request_Waypoint',
EDIT_WAYPOINT: 'Money_Request_Edit_Waypoint',
Expand Down
25 changes: 23 additions & 2 deletions src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) {
);
}

const hasStrikethroughStyle = 'textDecorationLine' in parentStyle && parentStyle.textDecorationLine === 'line-through';
const textDecorationLineStyle = hasStrikethroughStyle ? styles.underlineLineThrough : {};

return (
<AnchorForCommentsOnly
href={attrHref}
Expand All @@ -65,12 +68,30 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) {
// eslint-disable-next-line react/jsx-props-no-multi-spaces
target={htmlAttribs.target || '_blank'}
rel={htmlAttribs.rel || 'noopener noreferrer'}
style={[style, parentStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone]}
style={[style, parentStyle, textDecorationLineStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone]}
key={key}
// Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling
onPress={internalNewExpensifyPath || internalExpensifyPath ? () => Link.openLink(attrHref, environmentURL, isAttachment) : undefined}
>
<TNodeChildrenRenderer tnode={tnode} />
<TNodeChildrenRenderer
tnode={tnode}
renderChild={(props) => {
if (props.childTnode.tagName === 'br') {
return <Text key={props.key}>{'\n'}</Text>;
}
if (props.childTnode.type === 'text') {
return (
<Text
key={props.key}
style={[props.childTnode.getNativeStyles(), parentStyle, textDecorationLineStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone]}
>
{props.childTnode.data}
</Text>
);
}
return props.childElement;
}}
/>
</AnchorForCommentsOnly>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona
const htmlAttributeAccountID = tnode.attributes.accountid;

let accountID: number;
let displayNameOrLogin: string;
let mentionDisplayText: string;
let navigationRoute: Route;

const tnodeClone = cloneDeep(tnode);

const getMentionDisplayText = (displayText: string, userAccountID: string, userLogin = '') => {
const getShortMentionIfFound = (displayText: string, userAccountID: string, userLogin = '') => {
// If the userAccountID does not exist, this is an email-based mention so the displayText must be an email.
// If the userAccountID exists but userLogin is different from displayText, this means the displayText is either user display name, Hidden, or phone number, in which case we should return it as is.
if (userAccountID && userLogin !== displayText) {
Expand All @@ -59,18 +59,18 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona
if (!isEmpty(htmlAttribAccountID)) {
const user = personalDetails[htmlAttribAccountID];
accountID = parseInt(htmlAttribAccountID, 10);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
displayNameOrLogin = PersonalDetailsUtils.getDisplayNameOrDefault(user, LocalePhoneNumber.formatPhoneNumber(user?.login ?? ''));
mentionDisplayText = LocalePhoneNumber.formatPhoneNumber(user?.login ?? '') || PersonalDetailsUtils.getDisplayNameOrDefault(user);
mentionDisplayText = getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID, user?.login ?? '');
navigationRoute = ROUTES.PROFILE.getRoute(htmlAttribAccountID);
} else if ('data' in tnodeClone && !isEmptyObject(tnodeClone.data)) {
// We need to remove the LTR unicode and leading @ from data as it is not part of the login
displayNameOrLogin = tnodeClone.data.replace(CONST.UNICODE.LTR, '').slice(1);
mentionDisplayText = tnodeClone.data.replace(CONST.UNICODE.LTR, '').slice(1);
// We need to replace tnode.data here because we will pass it to TNodeChildrenRenderer below
asMutable(tnodeClone).data = tnodeClone.data.replace(displayNameOrLogin, Str.removeSMSDomain(getMentionDisplayText(displayNameOrLogin, htmlAttributeAccountID)));
asMutable(tnodeClone).data = tnodeClone.data.replace(mentionDisplayText, Str.removeSMSDomain(getShortMentionIfFound(mentionDisplayText, htmlAttributeAccountID)));

accountID = PersonalDetailsUtils.getAccountIDsByLogins([displayNameOrLogin])?.[0];
navigationRoute = ROUTES.DETAILS.getRoute(displayNameOrLogin);
displayNameOrLogin = Str.removeSMSDomain(displayNameOrLogin);
accountID = PersonalDetailsUtils.getAccountIDsByLogins([mentionDisplayText])?.[0];
navigationRoute = ROUTES.DETAILS.getRoute(mentionDisplayText);
mentionDisplayText = Str.removeSMSDomain(mentionDisplayText);
} else {
// If neither an account ID or email is provided, don't render anything
return null;
Expand All @@ -97,7 +97,7 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona
<UserDetailsTooltip
accountID={accountID}
fallbackUserDetails={{
displayName: displayNameOrLogin,
displayName: mentionDisplayText,
}}
>
<Text
Expand All @@ -108,7 +108,7 @@ function MentionUserRenderer({style, tnode, TDefaultRenderer, currentUserPersona
testID="span"
href={`/${navigationRoute}`}
>
{htmlAttribAccountID ? `@${displayNameOrLogin}` : <TNodeChildrenRenderer tnode={tnodeClone} />}
{htmlAttribAccountID ? `@${mentionDisplayText}` : <TNodeChildrenRenderer tnode={tnodeClone} />}
</Text>
</UserDetailsTooltip>
</Text>
Expand Down
22 changes: 11 additions & 11 deletions src/components/OptionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,14 @@ function OptionRow({
}

return (
<OfflineWithFeedback
pendingAction={option.pendingAction}
errors={option.allReportErrors}
shouldShowErrorMessages={false}
needsOffscreenAlphaCompositing
>
<Hoverable>
{(hovered) => (
<Hoverable>
{(hovered) => (
<OfflineWithFeedback
pendingAction={option.pendingAction}
errors={option.allReportErrors}
shouldShowErrorMessages={false}
needsOffscreenAlphaCompositing
>
<PressableWithFeedback
nativeID={keyForList}
ref={pressableRef}
Expand Down Expand Up @@ -314,9 +314,9 @@ function OptionRow({
</View>
)}
</PressableWithFeedback>
)}
</Hoverable>
</OfflineWithFeedback>
</OfflineWithFeedback>
)}
</Hoverable>
);
}

Expand Down
16 changes: 10 additions & 6 deletions src/components/ReportActionItem/ReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ function ReportPreview({

const isApproved = ReportUtils.isReportApproved(iouReport);
const canAllowSettlement = ReportUtils.hasUpdatedTotal(iouReport);
const allTransactions = TransactionUtils.getAllReportTransactions(iouReportID);
const transactionsWithReceipts = ReportUtils.getTransactionsWithReceipts(iouReportID);
const numberOfScanningReceipts = transactionsWithReceipts.filter((transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length;
const numberOfPendingRequests = transactionsWithReceipts.filter((transaction) => TransactionUtils.isPending(transaction) && TransactionUtils.isCardTransaction(transaction)).length;
Expand All @@ -138,14 +139,16 @@ function ReportPreview({
const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3);
const lastThreeReceipts = lastThreeTransactionsWithReceipts.map((transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction));

let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null;
let formattedMerchant = numberOfRequests === 1 ? TransactionUtils.getMerchant(allTransactions[0]) : null;
const formattedDescription = numberOfRequests === 1 ? TransactionUtils.getDescription(allTransactions[0]) : null;

if (TransactionUtils.isPartialMerchant(formattedMerchant ?? '')) {
formattedMerchant = null;
}
const previewSubtitle =
// Formatted merchant can be an empty string
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
formattedMerchant ||
(formattedMerchant ?? formattedDescription) ||
translate('iou.requestCount', {
count: numberOfRequests - numberOfScanningReceipts - numberOfPendingRequests,
scanningReceipts: numberOfScanningReceipts,
Expand Down Expand Up @@ -222,13 +225,14 @@ function ReportPreview({
/*
Show subtitle if at least one of the money requests is not being smart scanned, and either:
- There is more than one money request – in this case, the "X requests, Y scanning" subtitle is shown;
- There is only one money request, it has a receipt and is not being smart scanned – in this case, the request merchant is shown;
- There is only one money request, it has a receipt and is not being smart scanned – in this case, the request merchant or description is shown;
* There is an edge case when there is only one distance request with a pending route and amount = 0.
In this case, we don't want to show the merchant because it says: "Pending route...", which is already displayed in the amount field.
In this case, we don't want to show the merchant or description because it says: "Pending route...", which is already displayed in the amount field.
*/
const shouldShowSingleRequestMerchant = numberOfRequests === 1 && !!formattedMerchant && !(hasOnlyTransactionsWithPendingRoutes && !totalDisplaySpend);
const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchant || numberOfRequests > 1);
const shouldShowSingleRequestMerchantOrDescription =
numberOfRequests === 1 && (!!formattedMerchant || !!formattedDescription) && !(hasOnlyTransactionsWithPendingRoutes && !totalDisplaySpend);
const shouldShowSubtitle = !isScanning && (shouldShowSingleRequestMerchantOrDescription || numberOfRequests > 1);

return (
<OfflineWithFeedback
Expand Down
7 changes: 6 additions & 1 deletion src/components/RoomHeaderAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ import Text from './Text';
type RoomHeaderAvatarsProps = {
icons: Icon[];
reportID: string;
isGroupChat?: boolean;
};

function RoomHeaderAvatars({icons, reportID}: RoomHeaderAvatarsProps) {
function RoomHeaderAvatars({icons, reportID, isGroupChat}: RoomHeaderAvatarsProps) {
const navigateToAvatarPage = (icon: Icon) => {
if (isGroupChat) {
return;
}

if (icon.type === CONST.ICON_TYPE_WORKSPACE) {
Navigation.navigate(ROUTES.REPORT_AVATAR.getRoute(reportID));
return;
Expand Down
1 change: 0 additions & 1 deletion src/components/VideoPopoverMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ function VideoPopoverMenu({
anchorPosition={anchorPosition}
menuItems={menuItems}
anchorRef={videoPlayerMenuRef}
withoutOverlay
/>
);
}
Expand Down
39 changes: 39 additions & 0 deletions src/components/withNavigationTransitionEnd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {useNavigation} from '@react-navigation/native';
import type {StackNavigationProp} from '@react-navigation/stack';
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
import React, {useEffect, useState} from 'react';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import type {RootStackParamList} from '@libs/Navigation/types';

type WithNavigationTransitionEndProps = {didScreenTransitionEnd: boolean};

export default function <TProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>): React.ComponentType<TProps & RefAttributes<TRef>> {
function WithNavigationTransitionEnd(props: TProps, ref: ForwardedRef<TRef>) {
const [didScreenTransitionEnd, setDidScreenTransitionEnd] = useState(false);
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>();

useEffect(() => {
const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', () => {
setDidScreenTransitionEnd(true);
});

return unsubscribeTransitionEnd;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<WrappedComponent
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
didScreenTransitionEnd={didScreenTransitionEnd}
ref={ref}
/>
);
}

WithNavigationTransitionEnd.displayName = `WithNavigationTransitionEnd(${getComponentDisplayName(WrappedComponent)})`;

return React.forwardRef(WithNavigationTransitionEnd);
}

export type {WithNavigationTransitionEndProps};
2 changes: 1 addition & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ export default {
asCopilot: 'as copilot for',
},
mentionSuggestions: {
hereAlternateText: 'Notify everyone online in this room',
hereAlternateText: 'Notify everyone in this conversation',
},
newMessages: 'New messages',
reportTypingIndicator: {
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export default {
asCopilot: 'como copiloto de',
},
mentionSuggestions: {
hereAlternateText: 'Notificar a todos los que estén en linea de esta sala',
hereAlternateText: 'Notificar a todos en esta conversación',
},
newMessages: 'Mensajes nuevos',
reportTypingIndicator: {
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/StartSplitBillParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type StartSplitBillParams = {
isFromGroupDM: boolean;
createdReportActionID?: string;
billable: boolean;
chatType?: string;
};

export default StartSplitBillParams;
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ const MoneyRequestModalStackNavigator = createModalStackNavigator<MoneyRequestNa
[SCREENS.MONEY_REQUEST.STEP_TAG]: () => require('../../../../pages/iou/request/step/IOURequestStepTag').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.STEP_WAYPOINT]: () => require('../../../../pages/iou/request/step/IOURequestStepWaypoint').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.PARTICIPANTS]: () => require('../../../../pages/iou/steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.CONFIRMATION]: () => require('../../../../pages/iou/steps/MoneyRequestConfirmPage').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.CURRENCY]: () => require('../../../../pages/iou/IOUCurrencySelection').default as React.ComponentType,
[SCREENS.MONEY_REQUEST.HOLD]: () => require('../../../../pages/iou/HoldReasonPage').default as React.ComponentType,
[SCREENS.IOU_SEND.ADD_BANK_ACCOUNT]: () => require('../../../../pages/AddPersonalBankAccountPage').default as React.ComponentType,
Expand Down
Loading

0 comments on commit 8db0d5c

Please sign in to comment.