Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display Quick Action Button #38669

Merged
merged 55 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d98eacd
Retrieve quick action
Gonals Mar 19, 2024
a38a1b2
add helper functions
Gonals Mar 19, 2024
9ae46bb
add titles
Gonals Mar 19, 2024
3bc4a5a
Set titles
Gonals Mar 19, 2024
d724d36
add button
Gonals Mar 19, 2024
0d5489e
add avatars and title
Gonals Mar 20, 2024
583a397
Pss and display right avatar
Gonals Mar 20, 2024
9913831
place avatar correctly verticlly
Gonals Mar 20, 2024
f2c30a9
Display report title and add function
Gonals Mar 20, 2024
cf56da1
new params
Gonals Mar 20, 2024
bee853a
call correct routes
Gonals Mar 20, 2024
95724b0
Merge branch 'main' into alberto-qab
Gonals Mar 20, 2024
c95240c
spanish
Gonals Mar 20, 2024
7dd132d
use correct object key
Gonals Mar 20, 2024
56f0aaf
lint
Gonals Mar 20, 2024
2eb7a4d
fix tasks
Gonals Mar 20, 2024
62fc68f
use receipt icon
Gonals Mar 20, 2024
e438fdb
typescript
Gonals Mar 20, 2024
5bb163b
typo
Gonals Mar 20, 2024
d76ce0f
prettier
Gonals Mar 20, 2024
bc00787
spanish
Gonals Mar 20, 2024
98e8385
do not remove own avatar if it is the only one
Gonals Mar 20, 2024
e95214a
correct avatar size
Gonals Mar 20, 2024
a8419b2
reduce description to single line
Gonals Mar 20, 2024
b73e154
Fix second avatar border
Gonals Mar 21, 2024
184668e
make label hoverable
Gonals Mar 21, 2024
dc736de
non-hoverble
Gonals Mar 21, 2024
33735ca
typo
Gonals Mar 21, 2024
37cd485
use memo
Gonals Mar 21, 2024
8201e4c
missing dependencies
Gonals Mar 21, 2024
9428a61
prettier
Gonals Mar 21, 2024
844c4b8
dependencies
Gonals Mar 21, 2024
1bfe96d
Merge branch 'main' into alberto-qab
Gonals Mar 21, 2024
5f5356b
merge main
Gonals Mar 21, 2024
9da221a
investigte performnce issue
Gonals Mar 21, 2024
4db50be
more investigation
Gonals Mar 21, 2024
e557a48
back to riginal approach
Gonals Mar 21, 2024
c17cba6
dependencies
Gonals Mar 21, 2024
d89fa9a
Fix border
Gonals Mar 21, 2024
df90dc3
Merge branch 'main' into alberto-qab
Gonals Mar 21, 2024
b4a221f
Correct margins
Gonals Mar 21, 2024
a31cc59
Merge branch 'main' into alberto-qab
Gonals Mar 21, 2024
25db0ee
better styling
Gonals Mar 22, 2024
1bea6a6
conflicts
Gonals Mar 22, 2024
5b85cfb
conflicts
Gonals Mar 22, 2024
1f5f213
prettier
Gonals Mar 22, 2024
8137d9f
rename margin
Gonals Mar 22, 2024
ab8d07f
Merge branch 'main' into alberto-qab
Gonals Mar 25, 2024
6760426
Merge branch 'main' into alberto-qab
Gonals Mar 25, 2024
fe56250
use focus mode styles
Gonals Mar 25, 2024
399d95b
fix style
Gonals Mar 25, 2024
be686d3
style
Gonals Mar 25, 2024
f08a249
use subscript for workspaces
Gonals Mar 25, 2024
6b4a73e
pass correct value
Gonals Mar 25, 2024
97c8d0e
prettier
Gonals Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
457 changes: 245 additions & 212 deletions src/components/MenuItem.tsx

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions src/components/MultipleAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ function MultipleAvatars({
}),
[styles],
);

const secondAvatarStyle = secondAvatarStyleProp ?? [StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)];
const secondAvatarStyle = secondAvatarStyleProp ?? [StyleUtils.getBackgroundAndBorderStyle(isHovered ? theme.activeComponentBG : theme.componentBG)];

let avatarContainerStyles = StyleUtils.getContainerStyles(size, isInReportAction);
const {singleAvatarStyle, secondAvatarStyles} = useMemo(() => avatarSizeToStylesMap[size as AvatarSizeToStyles] ?? avatarSizeToStylesMap.default, [size, avatarSizeToStylesMap]);
Expand Down
6 changes: 6 additions & 0 deletions src/components/PopoverMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,17 @@ function PopoverMenu({
title={item.text}
shouldCheckActionAllowedOnPress={false}
description={item.description}
numberOfLinesDescription={item.numberOfLinesDescription}
onPress={() => selectItem(menuIndex)}
focused={focusedIndex === menuIndex}
displayInDefaultIconColor={item.displayInDefaultIconColor}
shouldShowRightIcon={item.shouldShowRightIcon}
shouldPutLeftPaddingWhenNoIcon={item.shouldPutLeftPaddingWhenNoIcon}
label={item.label}
isLabelHoverable={item.isLabelHoverable}
floatRightAvatars={item.floatRightAvatars}
floatRightAvatarSize={item.floatRightAvatarSize}
shouldShowSubscriptRightAvatar={item.shouldShowSubscriptRightAvatar}
disabled={item.disabled}
/>
))}
Expand Down
11 changes: 11 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,17 @@ export default {
deleteConfirmation: 'Are you sure you want to delete this receipt?',
addReceipt: 'Add receipt',
},
quickAction: {
scanReceipt: 'Scan Receipt',
recordDistance: 'Record Distance',
requestMoney: 'Request Money',
splitBill: 'Split Bill',
splitReceipt: 'Split Receipt',
splitDistance: 'Split Distance',
sendMoney: 'Send Money',
assignTask: 'Assign Task',
shortcut: 'Shortcut',
},
iou: {
amount: 'Amount',
taxAmount: 'Tax amount',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,17 @@ export default {
deleteConfirmation: '¿Estás seguro de que quieres borrar este recibo?',
addReceipt: 'Añadir recibo',
},
quickAction: {
scanReceipt: 'Escanear Recibo',
recordDistance: 'Grabar Distancia',
requestMoney: 'Solicitar Dinero',
splitBill: 'Dividir Cuenta',
splitReceipt: 'Dividir Recibo',
splitDistance: 'Dividir Distancia',
sendMoney: 'Enviar Dinero',
assignTask: 'Assignar Tarea',
shortcut: 'Acceso Directo',
},
iou: {
amount: 'Importe',
taxAmount: 'Importe del impuesto',
Expand Down
24 changes: 18 additions & 6 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,21 @@ function updateMoneyRequestTypeParams(routes: StackNavigationState<ParamListBase
}

// eslint-disable-next-line @typescript-eslint/naming-convention
function startMoneyRequest(iouType: ValueOf<typeof CONST.IOU.TYPE>, reportID: string) {
function startMoneyRequest(iouType: ValueOf<typeof CONST.IOU.TYPE>, reportID: string, requestType?: ValueOf<typeof CONST.IOU.REQUEST_TYPE>) {
clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID));
switch (requestType) {
case CONST.IOU.REQUEST_TYPE.MANUAL:
Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_MANUAL.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID));
return;
case CONST.IOU.REQUEST_TYPE.SCAN:
Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_SCAN.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID));
return;
case CONST.IOU.REQUEST_TYPE.DISTANCE:
Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_TAB_DISTANCE.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID));
return;
default:
Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, iouType, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, reportID));
}
}

// eslint-disable-next-line @typescript-eslint/naming-convention
Expand Down Expand Up @@ -583,7 +595,7 @@ function buildOnyxDataForMoneyRequest(
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
value: {
action: newQuickAction,
reportID: chatReport?.reportID,
chatReportID: chatReport?.reportID,
isFirstQuickAction: isEmptyObject(quickAction),
},
});
Expand Down Expand Up @@ -2329,7 +2341,7 @@ function createSplitsAndOnyxData(
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
value: {
action: iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE ? CONST.QUICK_ACTIONS.SPLIT_DISTANCE : CONST.QUICK_ACTIONS.SPLIT_MANUAL,
reportID: splitChatReport.reportID,
chatReportID: splitChatReport.reportID,
isFirstQuickAction: isEmptyObject(quickAction),
},
},
Expand Down Expand Up @@ -2815,7 +2827,7 @@ function startSplitBill(
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
value: {
action: CONST.QUICK_ACTIONS.SPLIT_SCAN,
reportID: splitChatReport.reportID,
chatReportID: splitChatReport.reportID,
isFirstQuickAction: isEmptyObject(quickAction),
},
},
Expand Down Expand Up @@ -4095,7 +4107,7 @@ function getSendMoneyParams(
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
value: {
action: CONST.QUICK_ACTIONS.SEND_MONEY,
reportID: chatReport.reportID,
chatReportID: chatReport.reportID,
isFirstQuickAction: isEmptyObject(quickAction),
},
};
Expand Down
8 changes: 6 additions & 2 deletions src/libs/actions/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ function createTaskAndNavigate(
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
value: {
action: CONST.QUICK_ACTIONS.ASSIGN_TASK,
reportID: parentReportID,
chatReportID: parentReportID,
isFirstQuickAction: isEmptyObject(quickAction),
targetAccountID: assigneeAccountID,
},
Expand Down Expand Up @@ -706,11 +706,15 @@ function setParentReportID(parentReportID: string) {
/**
* Clears out the task info from the store and navigates to the NewTaskDetails page
*/
function clearOutTaskInfoAndNavigate(reportID: string) {
function clearOutTaskInfoAndNavigate(reportID: string, accountID = 0) {
clearOutTaskInfo();
if (reportID && reportID !== '0') {
setParentReportID(reportID);
}
if (accountID > 0) {
const accountLogin = allPersonalDetails?.[accountID]?.login ?? '';
setAssigneeValue(accountLogin, accountID, reportID);
}
Navigation.navigate(ROUTES.NEW_TASK_DETAILS);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import PropTypes from 'prop-types';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react';
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import FloatingActionButton from '@components/FloatingActionButton';
import * as Expensicons from '@components/Icon/Expensicons';
import PopoverMenu from '@components/PopoverMenu';
Expand All @@ -16,6 +17,7 @@ import compose from '@libs/compose';
import interceptAnonymousUser from '@libs/interceptAnonymousUser';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportUtils from '@libs/ReportUtils';
import personalDetailsPropType from '@pages/personalDetailsPropType';
import * as App from '@userActions/App';
import * as IOU from '@userActions/IOU';
import * as Policy from '@userActions/Policy';
Expand All @@ -36,6 +38,50 @@ const policySelector = (policy) =>
pendingAction: policy.pendingAction,
};

const getQuickActionIcon = (action) => {
switch (action) {
case CONST.QUICK_ACTIONS.REQUEST_MANUAL:
return Expensicons.MoneyCircle;
case CONST.QUICK_ACTIONS.REQUEST_SCAN:
return Expensicons.Receipt;
case CONST.QUICK_ACTIONS.REQUEST_DISTANCE:
return Expensicons.Car;
case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
return Expensicons.Transfer;
case CONST.QUICK_ACTIONS.SEND_MONEY:
return Expensicons.Send;
case CONST.QUICK_ACTIONS.ASSIGN_TASK:
return Expensicons.Task;
default:
return Expensicons.MoneyCircle;
}
};

const getQuickActionTitle = (action) => {
switch (action) {
case CONST.QUICK_ACTIONS.REQUEST_MANUAL:
return 'quickAction.requestMoney';
case CONST.QUICK_ACTIONS.REQUEST_SCAN:
return 'quickAction.scanReceipt';
case CONST.QUICK_ACTIONS.REQUEST_DISTANCE:
return 'quickAction.recordDistance';
case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
return 'quickAction.splitBill';
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
return 'quickAction.splitReceipt';
case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
return 'quickAction.splitScan';
case CONST.QUICK_ACTIONS.SEND_MONEY:
return 'quickAction.sendMoney';
case CONST.QUICK_ACTIONS.ASSIGN_TASK:
return 'quickAction.assignTask';
default:
return '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid crashes (even if we've missed some action type), we should have added some default fallback key here instead of an empty string. This caused #38972

}
};

const propTypes = {
...windowDimensionsPropTypes,

Expand All @@ -56,13 +102,31 @@ const propTypes = {

/** Forwarded ref to FloatingActionButtonAndPopover */
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),

/** Information on the last taken action to display as Quick Action */
quickAction: PropTypes.shape({
action: PropTypes.string,
chatReportID: PropTypes.string,
targetAccountID: PropTypes.number,
isFirstQuickAction: PropTypes.bool,
Copy link
Contributor

@rlinoz rlinoz Mar 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where we are using the isFirstQuickAction, am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll be using that to determine if we show an educational tooltip about the quick action: #38054

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Not in use yet, but it'll come with the follow-up PRs

}),

/** Personal details of all the users */
personalDetails: personalDetailsPropType,

session: PropTypes.shape({
/** Currently logged in user accountID */
accountID: PropTypes.number,
}).isRequired,
};
const defaultProps = {
onHideCreateMenu: () => {},
onShowCreateMenu: () => {},
allPolicies: {},
isLoading: false,
innerRef: null,
quickAction: null,
personalDetails: {},
};

/**
Expand All @@ -80,6 +144,47 @@ function FloatingActionButtonAndPopover(props) {

const prevIsFocused = usePrevious(props.isFocused);

const quickActionReport = useMemo(() => (props.quickAction ? ReportUtils.getReport(props.quickAction.chatReportID) : 0), [props.quickAction]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line created a bug where the icon and group name is not updated correctly. More details here #40536


const quickActionAvatars = useMemo(() => {
if (quickActionReport) {
const avatars = ReportUtils.getIcons(quickActionReport, props.personalDetails);
return avatars.length <= 1 || ReportUtils.isPolicyExpenseChat(quickActionReport) ? avatars : _.filter(avatars, (avatar) => avatar.id !== props.session.accountID);
}
return [];
}, [props.personalDetails, props.session.accountID, quickActionReport]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were missing policy in the dependencies here, which caused #39045 where the avatar was not kept in sync if the workspace's avatar changed.


const navigateToQuickAction = () => {
switch (props.quickAction.action) {
case CONST.QUICK_ACTIONS.REQUEST_MANUAL:
IOU.startMoneyRequest(CONST.IOU.TYPE.REQUEST, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.MANUAL);
return;
case CONST.QUICK_ACTIONS.REQUEST_SCAN:
IOU.startMoneyRequest(CONST.IOU.TYPE.REQUEST, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.SCAN);
return;
case CONST.QUICK_ACTIONS.REQUEST_DISTANCE:
IOU.startMoneyRequest(CONST.IOU.TYPE.REQUEST, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.DISTANCE);
return;
case CONST.QUICK_ACTIONS.SPLIT_MANUAL:
IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.MANUAL);
return;
case CONST.QUICK_ACTIONS.SPLIT_SCAN:
IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.SCAN);
return;
case CONST.QUICK_ACTIONS.SPLIT_DISTANCE:
IOU.startMoneyRequest(CONST.IOU.TYPE.SPLIT, props.quickAction.chatReportID, CONST.IOU.REQUEST_TYPE.DISTANCE);
return;
case CONST.QUICK_ACTIONS.SEND_MONEY:
IOU.startMoneyRequest(CONST.IOU.TYPE.SEND, props.quickAction.chatReportID);
return;
case CONST.QUICK_ACTIONS.ASSIGN_TASK:
Task.clearOutTaskInfoAndNavigate(props.quickAction.chatReportID, _.get(props.quickAction, 'targetAccountID', 0));
return;
default:
return '';
}
};

/**
* Check if LHN status changed from active to inactive.
* Used to close already opened FAB menu when open any other pages (i.e. Press Command + K on web).
Expand Down Expand Up @@ -230,6 +335,22 @@ function FloatingActionButtonAndPopover(props) {
},
]
: []),
...(props.quickAction
? [
{
icon: getQuickActionIcon(props.quickAction.action),
text: translate(getQuickActionTitle(props.quickAction.action)),
label: translate('quickAction.shortcut'),
isLabelHoverable: false,
floatRightAvatars: quickActionAvatars,
floatRightAvatarSize: CONST.AVATAR_SIZE.SMALL,
description: ReportUtils.getReportName(quickActionReport),
numberOfLinesDescription: 1,
onSelected: () => interceptAnonymousUser(() => navigateToQuickAction()),
shouldShowSubscriptRightAvatar: ReportUtils.isPolicyExpenseChat(quickActionReport),
},
]
: []),
]}
withoutOverlay
anchorRef={fabRef}
Expand Down Expand Up @@ -271,5 +392,14 @@ export default compose(
isLoading: {
key: ONYXKEYS.IS_LOADING_APP,
},
quickAction: {
key: ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE,
},
personalDetails: {
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
},
Comment on lines +398 to +400
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use the hook for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, my bad. This is fine. I thought that this is current user's details which has a hook.

session: {
key: ONYXKEYS.SESSION,
},
}),
)(FloatingActionButtonAndPopoverWithRef);
4 changes: 4 additions & 0 deletions src/styles/utils/spacing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ export default {
marginRight: 32,
},

mrn2: {
marginRight: -8,
},

mrn5: {
marginRight: -20,
},
Expand Down
2 changes: 1 addition & 1 deletion src/types/onyx/QuickAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type QuickAction = {
action?: ValueOf<typeof CONST.QUICK_ACTIONS>;

/** ID of the report */
reportID?: string;
chatReportID?: string;

/** ID of the target account for task actions */
targetAccountID?: number;
Expand Down
Loading