From 034680f4d415f82ca3037eebb243bf61f6b42154 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Thu, 27 Jul 2023 06:39:57 +0500 Subject: [PATCH 1/8] feat: task view with offline with feedback --- src/components/ReportActionItem/TaskView.js | 58 +++++++++++++-------- src/libs/actions/Task.js | 19 ++++++- src/pages/home/report/ReportActionItem.js | 1 + 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 969f4c5e9591..fa7ee4e4f1bb 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'; @@ -10,6 +11,7 @@ import Navigation from '../../libs/Navigation/Navigation'; import ROUTES from '../../ROUTES'; import MenuItemWithTopDescription from '../MenuItemWithTopDescription'; 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'; @@ -66,7 +68,9 @@ function TaskView(props) { accessibilityLabel={taskTitle || props.translate('task.task')} > {({hovered, pressed}) => ( - <> + {props.translate('task.title')} )} - + )} - 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))} + + Navigation.navigate(ROUTES.getTaskReportDescriptionRoute(props.report.reportID))} shouldShowRightIcon={isOpen} disabled={disableState} - wrapperStyle={[styles.pv2]} - isSmallAvatarSubscriptMenu + 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} + /> + ) : ( Date: Thu, 27 Jul 2023 16:44:44 +0500 Subject: [PATCH 2/8] feat: reset pending fields for failure data --- src/libs/actions/Task.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 021afe28e4b1..fd9dca3c455e 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -431,7 +431,15 @@ 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, + ...(title && {reportName: null}), + ...(description && {description: null}), + ...(assigneeAccountID && {managerID: null}), + }, }, ]; From 294a46c48b1b9ae1336d51b1ceec00116b8bb887 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Thu, 27 Jul 2023 16:47:21 +0500 Subject: [PATCH 3/8] feat: add pending fields wrapper --- src/libs/actions/Task.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index fd9dca3c455e..491da4dfe405 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -436,9 +436,11 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign description: report.description, assignee: report.managerEmail, assigneeAccountID: report.managerID, - ...(title && {reportName: null}), - ...(description && {description: null}), - ...(assigneeAccountID && {managerID: null}), + pendingFields: { + ...(title && {reportName: null}), + ...(description && {description: null}), + ...(assigneeAccountID && {managerID: null}), + }, }, }, ]; From 7f84799abfbd70de22ccd262daa9bbeacddecbcf Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 28 Jul 2023 05:43:26 +0500 Subject: [PATCH 4/8] feat: add error state to tasks --- src/components/ReportActionItem/TaskView.js | 18 ++++++++++------ src/libs/actions/Task.js | 24 ++++++++++++++------- src/pages/home/report/ReportActionItem.js | 1 + src/pages/reportPropTypes.js | 3 +++ 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index cc624da8af4b..84f8215e9156 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -54,7 +54,12 @@ function TaskView(props) { const canEdit = PolicyUtils.isPolicyAdmin(policy) || Task.isTaskAssigneeOrTaskOwner(props.report, props.currentUserPersonalDetails.accountID); const disableState = !canEdit || !isOpen; return ( - + Task.clearEditTaskErrors(props.report)} + errorRowStyles={styles.ph5} + > {(hovered) => ( {({pressed}) => ( - + {props.translate('task.title')} )} - + {props.report.managerID ? ( - + )} - - {props.shouldShowHorizontalRule && } - + {props.shouldShowHorizontalRule && } + ); } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 491da4dfe405..29855ec0f8d8 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -405,7 +405,7 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign ...(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}), - }, + } }, }, ]; @@ -418,7 +418,7 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign ...(title && {reportName: null}), ...(description && {description: null}), ...(assigneeAccountID && {managerID: null}), - }, + } }, }, ]; @@ -436,13 +436,8 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign description: report.description, assignee: report.managerEmail, assigneeAccountID: report.managerID, - pendingFields: { - ...(title && {reportName: null}), - ...(description && {description: null}), - ...(assigneeAccountID && {managerID: null}), - }, }, - }, + } ]; if (optimisticAssigneeAddComment) { @@ -756,6 +751,18 @@ function isTaskAssigneeOrTaskOwner(taskReport, sessionAccountID) { return sessionAccountID === getTaskOwnerAccountID(taskReport) || sessionAccountID === getTaskAssigneeAccountID(taskReport); } +/** + * Clears any possible stored errors for a specific field on a task report + * + * @param {Object} report + */ +function clearEditTaskErrors(report) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, { + pendingFields: null, + errorFields: null, + }); +} + export { createTaskAndNavigate, editTaskAndNavigate, @@ -775,4 +782,5 @@ export { dismissModalAndClearOutTaskInfo, getTaskAssigneeAccountID, isTaskAssigneeOrTaskOwner, + clearEditTaskErrors }; diff --git a/src/pages/home/report/ReportActionItem.js b/src/pages/home/report/ReportActionItem.js index d9fadf25b33f..e88e5c811803 100644 --- a/src/pages/home/report/ReportActionItem.js +++ b/src/pages/home/report/ReportActionItem.js @@ -608,6 +608,7 @@ export default compose( _.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/reportPropTypes.js b/src/pages/reportPropTypes.js index 2ddf82795c33..5992de09dc4f 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)), }); From 19077e1c6d09241f00bb3b924f66ca188a8fb4c7 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 28 Jul 2023 06:31:40 +0500 Subject: [PATCH 5/8] feat: trigger re-render if errorFields are not equal --- src/pages/home/report/ReportActionsView.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/home/report/ReportActionsView.js b/src/pages/home/report/ReportActionsView.js index 22d2fcf08655..9ff1df36dd6a 100755 --- a/src/pages/home/report/ReportActionsView.js +++ b/src/pages/home/report/ReportActionsView.js @@ -352,6 +352,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; } From 31b79e6ad48dc9812ac1eb04f129672041e304fb Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Fri, 28 Jul 2023 06:35:51 +0500 Subject: [PATCH 6/8] feat: lint errors --- src/components/ReportActionItem/TaskView.js | 10 +++++----- src/libs/actions/Task.js | 8 ++++---- src/pages/reportPropTypes.js | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index 84f8215e9156..fc037fdf3377 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -54,9 +54,9 @@ function TaskView(props) { const canEdit = PolicyUtils.isPolicyAdmin(policy) || Task.isTaskAssigneeOrTaskOwner(props.report, props.currentUserPersonalDetails.accountID); const disableState = !canEdit || !isOpen; return ( - Task.clearEditTaskErrors(props.report)} errorRowStyles={styles.ph5} > @@ -151,8 +151,8 @@ function TaskView(props) { shouldGreyOutWhenDisabled={false} /> )} - {props.shouldShowHorizontalRule && } - + {props.shouldShowHorizontalRule && } + ); } diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 29855ec0f8d8..be9e8a30f97a 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -405,7 +405,7 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign ...(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}), - } + }, }, }, ]; @@ -418,7 +418,7 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign ...(title && {reportName: null}), ...(description && {description: null}), ...(assigneeAccountID && {managerID: null}), - } + }, }, }, ]; @@ -437,7 +437,7 @@ function editTaskAndNavigate(report, ownerAccountID, {title, description, assign assignee: report.managerEmail, assigneeAccountID: report.managerID, }, - } + }, ]; if (optimisticAssigneeAddComment) { @@ -782,5 +782,5 @@ export { dismissModalAndClearOutTaskInfo, getTaskAssigneeAccountID, isTaskAssigneeOrTaskOwner, - clearEditTaskErrors + clearEditTaskErrors, }; diff --git a/src/pages/reportPropTypes.js b/src/pages/reportPropTypes.js index 5992de09dc4f..58b5ffe6ee2b 100644 --- a/src/pages/reportPropTypes.js +++ b/src/pages/reportPropTypes.js @@ -77,6 +77,6 @@ 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)), + /** Field-specific pending states for offline UI status */ + pendingFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)), }); From 14198e8141899382ca4f1b07dfcdb4b1e9f15ca8 Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Sat, 29 Jul 2023 02:45:49 +0500 Subject: [PATCH 7/8] feat: move the error dot above the horizontal line in task view --- src/components/ReportActionItem/TaskView.js | 182 ++++++++++---------- src/libs/actions/Task.js | 8 +- 2 files changed, 95 insertions(+), 95 deletions(-) diff --git a/src/components/ReportActionItem/TaskView.js b/src/components/ReportActionItem/TaskView.js index fc037fdf3377..2e45439a43ac 100644 --- a/src/components/ReportActionItem/TaskView.js +++ b/src/components/ReportActionItem/TaskView.js @@ -54,105 +54,107 @@ function TaskView(props) { const canEdit = PolicyUtils.isPolicyAdmin(policy) || Task.isTaskAssigneeOrTaskOwner(props.report, props.currentUserPersonalDetails.accountID); const disableState = !canEdit || !isOpen; return ( - Task.clearEditTaskErrors(props.report)} - errorRowStyles={styles.ph5} - > - - {(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 || !canEdit} - /> - - - {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 || !canEdit} + /> + + + {taskTitle} + - )} - - - )} - - )} - - - 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.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]} - 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 be9e8a30f97a..34a6519b94b5 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -752,12 +752,10 @@ function isTaskAssigneeOrTaskOwner(taskReport, sessionAccountID) { } /** - * Clears any possible stored errors for a specific field on a task report - * - * @param {Object} report + * @param {String} reportID */ -function clearEditTaskErrors(report) { - Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, { +function clearEditTaskErrors(reportID) { + Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, { pendingFields: null, errorFields: null, }); From cdd9fbadc1d0eb2f0b3bd65cc2507cbf66286f8e Mon Sep 17 00:00:00 2001 From: Sibtain Ali Date: Thu, 3 Aug 2023 12:41:35 +0500 Subject: [PATCH 8/8] feat: remove isAssignee function --- src/libs/actions/Task.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/actions/Task.js b/src/libs/actions/Task.js index 8d8bd079be2c..56aa9ab09083 100644 --- a/src/libs/actions/Task.js +++ b/src/libs/actions/Task.js @@ -787,7 +787,6 @@ export { cancelTask, dismissModalAndClearOutTaskInfo, getTaskAssigneeAccountID, - isTaskAssigneeOrTaskOwner, clearEditTaskErrors, canModifyTask, };