Skip to content

Commit

Permalink
Merge pull request #23705 from allroundexperts/fix-23200
Browse files Browse the repository at this point in the history
feat: task view with offline with feedback
  • Loading branch information
neil-marcellini authored Aug 3, 2023
2 parents ec7b267 + cdd9fba commit d33b92d
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 87 deletions.
182 changes: 97 additions & 85 deletions src/components/ReportActionItem/TaskView.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useEffect} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import reportPropTypes from '../../pages/reportPropTypes';
import withLocalize, {withLocalizePropTypes} from '../withLocalize';
import withWindowDimensions from '../withWindowDimensions';
Expand All @@ -11,6 +12,7 @@ import ROUTES from '../../ROUTES';
import MenuItemWithTopDescription from '../MenuItemWithTopDescription';
import Hoverable from '../Hoverable';
import MenuItem from '../MenuItem';
import OfflineWithFeedback from '../OfflineWithFeedback';
import styles from '../../styles/styles';
import * as ReportUtils from '../../libs/ReportUtils';
import * as OptionsListUtils from '../../libs/OptionsListUtils';
Expand Down Expand Up @@ -51,94 +53,104 @@ function TaskView(props) {
const disableState = !canModifyTask || !isOpen;
return (
<View>
<Hoverable>
{(hovered) => (
<PressableWithSecondaryInteraction
onPress={Session.checkIfActionIsAllowed((e) => {
if (e && e.type === 'click') {
e.currentTarget.blur();
}
<OfflineWithFeedback
shouldShowErrorMessages
errors={lodashGet(props, 'report.errorFields.editTask')}
onClose={() => Task.clearEditTaskErrors(props.report.reportID)}
errorRowStyles={styles.ph5}
>
<Hoverable>
{(hovered) => (
<PressableWithSecondaryInteraction
onPress={Session.checkIfActionIsAllowed((e) => {
if (e && e.type === 'click') {
e.currentTarget.blur();
}

Navigation.navigate(ROUTES.getTaskReportTitleRoute(props.report.reportID));
})}
style={({pressed}) => [styles.ph5, styles.pv2, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState), true)]}
ref={props.forwardedRef}
disabled={disableState}
accessibilityLabel={taskTitle || props.translate('task.task')}
>
{({pressed}) => (
<>
<Text style={styles.taskTitleDescription}>{props.translate('task.title')}</Text>
<View style={[styles.flexRow, styles.alignItemsTop, styles.flex1]}>
<Checkbox
onPress={() => (isCompleted ? Task.reopenTask(props.report.reportID, taskTitle) : Task.completeTask(props.report.reportID, taskTitle))}
isChecked={isCompleted}
style={styles.taskMenuItemCheckbox}
containerSize={24}
containerBorderRadius={8}
caretSize={16}
accessibilityLabel={taskTitle || props.translate('task.task')}
disabled={isCanceled || !canModifyTask}
/>
<View style={[styles.flexRow, styles.flex1]}>
<Text
numberOfLines={3}
style={styles.taskTitleMenuItem}
>
{taskTitle}
</Text>
</View>
{isOpen && (
<View style={styles.taskRightIconContainer}>
<Icon
additionalStyles={[styles.alignItemsCenter]}
src={Expensicons.ArrowRight}
fill={StyleUtils.getIconFillColor(getButtonState(hovered, pressed, false, disableState))}
/>
Navigation.navigate(ROUTES.getTaskReportTitleRoute(props.report.reportID));
})}
style={({pressed}) => [styles.ph5, styles.pv2, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed, false, disableState), true)]}
ref={props.forwardedRef}
disabled={disableState}
accessibilityLabel={taskTitle || props.translate('task.task')}
>
{({pressed}) => (
<OfflineWithFeedback pendingAction={lodashGet(props, 'report.pendingFields.reportName')}>
<Text style={styles.taskTitleDescription}>{props.translate('task.title')}</Text>
<View style={[styles.flexRow, styles.alignItemsTop, styles.flex1]}>
<Checkbox
onPress={() => (isCompleted ? Task.reopenTask(props.report.reportID, taskTitle) : Task.completeTask(props.report.reportID, taskTitle))}
isChecked={isCompleted}
style={styles.taskMenuItemCheckbox}
containerSize={24}
containerBorderRadius={8}
caretSize={16}
accessibilityLabel={taskTitle || props.translate('task.task')}
disabled={isCanceled || !canModifyTask}
/>
<View style={[styles.flexRow, styles.flex1]}>
<Text
numberOfLines={3}
style={styles.taskTitleMenuItem}
>
{taskTitle}
</Text>
</View>
)}
</View>
</>
)}
</PressableWithSecondaryInteraction>
{isOpen && (
<View style={styles.taskRightIconContainer}>
<Icon
additionalStyles={[styles.alignItemsCenter]}
src={Expensicons.ArrowRight}
fill={StyleUtils.getIconFillColor(getButtonState(hovered, pressed, false, disableState))}
/>
</View>
)}
</View>
</OfflineWithFeedback>
)}
</PressableWithSecondaryInteraction>
)}
</Hoverable>
<OfflineWithFeedback pendingAction={lodashGet(props, 'report.pendingFields.description')}>
<MenuItemWithTopDescription
description={props.translate('task.description')}
title={props.report.description || ''}
onPress={() => Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
shouldGreyOutWhenDisabled={false}
numberOfLinesTitle={0}
/>
</OfflineWithFeedback>
{props.report.managerID ? (
<OfflineWithFeedback pendingAction={lodashGet(props, 'report.pendingFields.managerID')}>
<MenuItem
label={props.translate('task.assignee')}
title={ReportUtils.getDisplayNameForParticipant(props.report.managerID)}
icon={OptionsListUtils.getAvatarsForAccountIDs([props.report.managerID], props.personalDetails)}
iconType={CONST.ICON_TYPE_AVATAR}
avatarSize={CONST.AVATAR_SIZE.SMALLER}
titleStyle={styles.assigneeTextStyle}
onPress={() => Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2]}
isSmallAvatarSubscriptMenu
shouldGreyOutWhenDisabled={false}
/>
</OfflineWithFeedback>
) : (
<MenuItemWithTopDescription
description={props.translate('task.assignee')}
onPress={() => Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2]}
shouldGreyOutWhenDisabled={false}
/>
)}
</Hoverable>
<MenuItemWithTopDescription
description={props.translate('task.description')}
title={props.report.description || ''}
onPress={() => Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]}
shouldGreyOutWhenDisabled={false}
numberOfLinesTitle={0}
/>
{props.report.managerID ? (
<MenuItem
label={props.translate('task.assignee')}
title={ReportUtils.getDisplayNameForParticipant(props.report.managerID)}
icon={OptionsListUtils.getAvatarsForAccountIDs([props.report.managerID], props.personalDetails)}
iconType={CONST.ICON_TYPE_AVATAR}
avatarSize={CONST.AVATAR_SIZE.SMALLER}
titleStyle={styles.assigneeTextStyle}
onPress={() => Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2]}
isSmallAvatarSubscriptMenu
shouldGreyOutWhenDisabled={false}
/>
) : (
<MenuItemWithTopDescription
description={props.translate('task.assignee')}
onPress={() => Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))}
shouldShowRightIcon={isOpen}
disabled={disableState}
wrapperStyle={[styles.pv2]}
shouldGreyOutWhenDisabled={false}
/>
)}

</OfflineWithFeedback>
{props.shouldShowHorizontalRule && <View style={styles.reportHorizontalRule} />}
</View>
);
Expand Down
37 changes: 35 additions & 2 deletions src/libs/actions/Task.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,10 +401,27 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign
description: reportDescription,
managerID: assigneeAccountID || report.managerID,
managerEmail: assignee || report.managerEmail,
pendingFields: {
...(title && {reportName: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(description && {description: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
...(assigneeAccountID && {managerID: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE}),
},
},
},
];
const successData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`,
value: {
pendingFields: {
...(title && {reportName: null}),
...(description && {description: null}),
...(assigneeAccountID && {managerID: null}),
},
},
},
];
const successData = [];
const failureData = [
{
onyxMethod: Onyx.METHOD.MERGE,
Expand All @@ -414,7 +431,12 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`,
value: {reportName: report.reportName, description: report.description, assignee: report.managerEmail, assigneeAccountID: report.managerID},
value: {
reportName: report.reportName,
description: report.description,
assignee: report.managerEmail,
assigneeAccountID: report.managerID,
},
},
];

Expand Down Expand Up @@ -737,6 +759,16 @@ function canModifyTask(taskReport, sessionAccountID) {
return ReportUtils.isAllowedToComment(parentReport);
}

/**
* @param {String} reportID
*/
function clearEditTaskErrors(reportID) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {
pendingFields: null,
errorFields: null,
});
}

export {
createTaskAndNavigate,
editTaskAndNavigate,
Expand All @@ -755,5 +787,6 @@ export {
cancelTask,
dismissModalAndClearOutTaskInfo,
getTaskAssigneeAccountID,
clearEditTaskErrors,
canModifyTask,
};
2 changes: 2 additions & 0 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,8 @@ export default compose(
prevProps.shouldDisplayNewMarker === nextProps.shouldDisplayNewMarker &&
_.isEqual(prevProps.emojiReactions, nextProps.emojiReactions) &&
_.isEqual(prevProps.action, nextProps.action) &&
_.isEqual(prevProps.report.pendingFields, nextProps.report.pendingFields) &&
_.isEqual(prevProps.report.errorFields, nextProps.report.errorFields) &&
lodashGet(prevProps.report, 'statusNum') === lodashGet(nextProps.report, 'statusNum') &&
lodashGet(prevProps.report, 'stateNum') === lodashGet(nextProps.report, 'stateNum') &&
prevProps.translate === nextProps.translate &&
Expand Down
8 changes: 8 additions & 0 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,14 @@ function arePropsEqual(oldProps, newProps) {
return false;
}

if (!_.isEqual(oldProps.report.pendingFields, newProps.report.pendingFields)) {
return false;
}

if (!_.isEqual(oldProps.report.errorFields, newProps.report.errorFields)) {
return false;
}

if (lodashGet(oldProps.network, 'isOffline') !== lodashGet(newProps.network, 'isOffline')) {
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/pages/reportPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,7 @@ export default PropTypes.shape({

/** Which user role is capable of posting messages on the report */
writeCapability: PropTypes.oneOf(_.values(CONST.REPORT.WRITE_CAPABILITIES)),

/** Field-specific pending states for offline UI status */
pendingFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
});

0 comments on commit d33b92d

Please sign in to comment.