diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index a08f341acb1f..1bb34e22c36b 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -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'; @@ -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'; @@ -51,94 +53,104 @@ function TaskView(props) { const disableState = !canModifyTask || !isOpen; return ( - - {(hovered) => ( - { - if (e && e.type === 'click') { - e.currentTarget.blur(); - } + Task.clearEditTaskErrors(props.report.reportID)} + errorRowStyles={styles.ph5} + > + + {(hovered) => ( + { + 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}) => ( - <> - {props.translate('task.title')} - - (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} - /> - - - {taskTitle} - - - {isOpen && ( - - + 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}) => ( + + {props.translate('task.title')} + + (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} + /> + + + {taskTitle} + - )} - - - )} - + {isOpen && ( + + + + )} + + + )} + + )} + + + Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))} + shouldShowRightIcon={isOpen} + disabled={disableState} + wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} + shouldGreyOutWhenDisabled={false} + numberOfLinesTitle={0} + /> + + {props.report.managerID ? ( + + Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))} + shouldShowRightIcon={isOpen} + disabled={disableState} + wrapperStyle={[styles.pv2]} + isSmallAvatarSubscriptMenu + shouldGreyOutWhenDisabled={false} + /> + + ) : ( + Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))} + shouldShowRightIcon={isOpen} + disabled={disableState} + wrapperStyle={[styles.pv2]} + shouldGreyOutWhenDisabled={false} + /> )} - - Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))} - shouldShowRightIcon={isOpen} - disabled={disableState} - wrapperStyle={[styles.pv2, styles.taskDescriptionMenuItem]} - shouldGreyOutWhenDisabled={false} - numberOfLinesTitle={0} - /> - {props.report.managerID ? ( - Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))} - shouldShowRightIcon={isOpen} - disabled={disableState} - wrapperStyle={[styles.pv2]} - isSmallAvatarSubscriptMenu - shouldGreyOutWhenDisabled={false} - /> - ) : ( - Navigation.navigate(ROUTES.getTaskReportAssigneeRoute(props.report.reportID))} - shouldShowRightIcon={isOpen} - disabled={disableState} - wrapperStyle={[styles.pv2]} - shouldGreyOutWhenDisabled={false} - /> - )} - + {props.shouldShowHorizontalRule && } ); diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 850149a83635..56aa9ab09083 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -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, @@ -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, + }, }, ]; @@ -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, @@ -755,5 +787,6 @@ export { cancelTask, dismissModalAndClearOutTaskInfo, getTaskAssigneeAccountID, + clearEditTaskErrors, canModifyTask, }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index 8c6defcb2951..2a9abab4681d 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -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 && diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index d96c5dba30e8..8ab348476ad6 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -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; } diff --git a/src/pages/reportPropTypes.js b/src/pages/reportPropTypes.js index 2ddf82795c33..58b5ffe6ee2b 100644 --- a/src/pages/reportPropTypes.js +++ b/src/pages/reportPropTypes.js @@ -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)), });