diff --git a/src/components/ReportActionItem/TaskPreview.js b/src/components/ReportActionItem/TaskPreview.tsx similarity index 50% rename from src/components/ReportActionItem/TaskPreview.js rename to src/components/ReportActionItem/TaskPreview.tsx index 414f030d4fc7..fbc58a381318 100644 --- a/src/components/ReportActionItem/TaskPreview.js +++ b/src/components/ReportActionItem/TaskPreview.tsx @@ -1,22 +1,20 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; +import Str from 'expensify-common/lib/str'; import React from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {OnyxEntry} from 'react-native-onyx'; import Checkbox from '@components/Checkbox'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; import {usePersonalDetails} from '@components/OnyxProvider'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; -import refPropTypes from '@components/refPropTypes'; import RenderHTML from '@components/RenderHTML'; import {showContextMenuForReport} from '@components/ShowContextMenuContext'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; -import compose from '@libs/compose'; import ControlSelection from '@libs/ControlSelection'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import getButtonState from '@libs/getButtonState'; @@ -24,144 +22,136 @@ import * as LocalePhoneNumber from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; import * as ReportUtils from '@libs/ReportUtils'; import * as TaskUtils from '@libs/TaskUtils'; -import reportActionPropTypes from '@pages/home/report/reportActionPropTypes'; import * as Session from '@userActions/Session'; import * as Task from '@userActions/Task'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {Policy, Report, ReportAction} from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; -const propTypes = { - /** The ID of the associated taskReport */ - taskReportID: PropTypes.string.isRequired, - - /** Whether the task preview is hovered so we can modify its style */ - isHovered: PropTypes.bool, - - /** The linked reportAction */ - action: PropTypes.shape(reportActionPropTypes).isRequired, +type PolicyRole = { + /** The role of current user */ + role: string; +}; +type TaskPreviewOnyxProps = { /* Onyx Props */ - taskReport: PropTypes.shape({ - /** Title of the task */ - reportName: PropTypes.string, - - /** AccountID of the manager in this iou report */ - managerID: PropTypes.number, - - /** AccountID of the creator of this iou report */ - ownerAccountID: PropTypes.number, - }), + /* current report of TaskPreview */ + taskReport: OnyxEntry; /** The policy of root parent report */ - rootParentReportpolicy: PropTypes.shape({ - /** The role of current user */ - role: PropTypes.string, - }), - - /** The chat report associated with taskReport */ - chatReportID: PropTypes.string.isRequired, - - /** Popover context menu anchor, used for showing context menu */ - contextMenuAnchor: refPropTypes, - - /** Callback for updating context menu active state, used for showing context menu */ - checkIfContextMenuActive: PropTypes.func, - - /* Onyx Props */ - ...withLocalizePropTypes, - - ...withCurrentUserPersonalDetailsPropTypes, -}; - -const defaultProps = { - ...withCurrentUserPersonalDetailsDefaultProps, - taskReport: {}, - rootParentReportpolicy: {}, - isHovered: false, + rootParentReportpolicy: OnyxEntry; }; -function TaskPreview(props) { +type TaskPreviewProps = WithCurrentUserPersonalDetailsProps & + TaskPreviewOnyxProps & { + /** The ID of the associated policy */ + // eslint-disable-next-line react/no-unused-prop-types + policyID: string; + /** The ID of the associated taskReport */ + taskReportID: string; + + /** Whether the task preview is hovered so we can modify its style */ + isHovered: boolean; + + /** The linked reportAction */ + action: OnyxEntry; + + /** The chat report associated with taskReport */ + chatReportID: string; + + /** Popover context menu anchor, used for showing context menu */ + contextMenuAnchor: Element; + + /** Callback for updating context menu active state, used for showing context menu */ + checkIfContextMenuActive: () => void; + }; + +function TaskPreview({ + taskReport, + taskReportID, + action, + contextMenuAnchor, + chatReportID, + checkIfContextMenuActive, + currentUserPersonalDetails, + rootParentReportpolicy, + isHovered = false, +}: TaskPreviewProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const personalDetails = usePersonalDetails() || CONST.EMPTY_OBJECT; + const {translate} = useLocalize(); // The reportAction might not contain details regarding the taskReport // Only the direct parent reportAction will contain details about the taskReport // Other linked reportActions will only contain the taskReportID and we will grab the details from there - const isTaskCompleted = !_.isEmpty(props.taskReport) - ? props.taskReport.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && props.taskReport.statusNum === CONST.REPORT.STATUS.APPROVED - : props.action.childStateNum === CONST.REPORT.STATE_NUM.SUBMITTED && props.action.childStatusNum === CONST.REPORT.STATUS.APPROVED; - const taskTitle = _.escape(TaskUtils.getTaskTitle(props.taskReportID, props.action.childReportName)); - const taskAssigneeAccountID = Task.getTaskAssigneeAccountID(props.taskReport) || props.action.childManagerAccountID; - const assigneeLogin = lodashGet(personalDetails, [taskAssigneeAccountID, 'login'], ''); - const assigneeDisplayName = lodashGet(personalDetails, [taskAssigneeAccountID, 'displayName'], ''); + const isTaskCompleted = !isEmptyObject(taskReport) + ? taskReport?.stateNum === CONST.REPORT.STATE_NUM.SUBMITTED && taskReport.statusNum === CONST.REPORT.STATUS.APPROVED + : action?.childStateNum === CONST.REPORT.STATE_NUM.SUBMITTED && action?.childStatusNum === CONST.REPORT.STATUS.APPROVED; + const taskTitle = Str.htmlEncode(TaskUtils.getTaskTitle(taskReportID, action?.childReportName ?? '')); + const taskAssigneeAccountID = Task.getTaskAssigneeAccountID(taskReport ?? {}) ?? action?.childManagerAccountID ?? ''; + const assigneeLogin = personalDetails[taskAssigneeAccountID]?.login ?? ''; + const assigneeDisplayName = personalDetails[taskAssigneeAccountID]?.displayName ?? ''; const taskAssignee = assigneeDisplayName || LocalePhoneNumber.formatPhoneNumber(assigneeLogin); const htmlForTaskPreview = taskAssignee && taskAssigneeAccountID !== 0 ? `@${taskAssignee} ${taskTitle}` : `${taskTitle}`; - const isDeletedParentAction = ReportUtils.isCanceledTaskReport(props.taskReport, props.action); + const isDeletedParentAction = ReportUtils.isCanceledTaskReport(taskReport, action); if (isDeletedParentAction) { - return ${props.translate('parentReportAction.deletedTask')}`} />; + return ${translate('parentReportAction.deletedTask')}`} />; } return ( Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(props.taskReportID))} + onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(taskReportID))} onPressIn={() => DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()} onPressOut={() => ControlSelection.unblock()} - onLongPress={(event) => showContextMenuForReport(event, props.contextMenuAnchor, props.chatReportID, props.action, props.checkIfContextMenuActive)} + onLongPress={(event) => showContextMenuForReport(event, contextMenuAnchor, chatReportID, action ?? {}, checkIfContextMenuActive)} style={[styles.flexRow, styles.justifyContentBetween]} role={CONST.ROLE.BUTTON} - accessibilityLabel={props.translate('task.task')} + accessibilityLabel={translate('task.task')} > { if (isTaskCompleted) { - Task.reopenTask(props.taskReport); + Task.reopenTask(taskReport ?? {}); } else { - Task.completeTask(props.taskReport); + Task.completeTask(taskReport ?? {}); } })} - accessibilityLabel={props.translate('task.task')} + accessibilityLabel={translate('task.task')} /> ); } -TaskPreview.propTypes = propTypes; -TaskPreview.defaultProps = defaultProps; TaskPreview.displayName = 'TaskPreview'; -export default compose( - withLocalize, - withCurrentUserPersonalDetails, - withOnyx({ +export default withCurrentUserPersonalDetails( + withOnyx({ taskReport: { key: ({taskReportID}) => `${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`, - initialValue: {}, }, rootParentReportpolicy: { - key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID || '0'}`, - selector: (policy) => _.pick(policy, ['role']), + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? '0'}`, + selector: (policy: Policy | null) => ({role: policy?.role ?? ''}), }, - }), -)(TaskPreview); + })(TaskPreview), +);