Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
159 changes: 75 additions & 84 deletions src/pages/FlagCommentPage.js → src/pages/FlagCommentPage.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,150 @@
import PropTypes from 'prop-types';
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useCallback} from 'react';
import {ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import _ from 'underscore';
import type {OnyxEntry} from 'react-native-onyx';
import type {SvgProps} from 'react-native-svg';
import type {ValueOf} from 'type-fest';
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import MenuItem from '@components/MenuItem';
import ScreenWrapper from '@components/ScreenWrapper';
import Text from '@components/Text';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import Navigation from '@libs/Navigation/Navigation';
import type {FlagCommentNavigatorParamList} from '@libs/Navigation/types';
import * as ReportUtils from '@libs/ReportUtils';
import * as Report from '@userActions/Report';
import * as Session from '@userActions/Session';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import reportActionPropTypes from './home/report/reportActionPropTypes';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import withReportAndReportActionOrNotFound from './home/report/withReportAndReportActionOrNotFound';
import reportPropTypes from './reportPropTypes';
import type {WithReportAndReportActionOrNotFoundProps} from './home/report/withReportAndReportActionOrNotFound';

const propTypes = {
/** Array of report actions for this report */
reportActions: PropTypes.shape(reportActionPropTypes),

/** The active report */
report: reportPropTypes,
type FlagCommentPageWithOnyxProps = {
/** All the report actions from the parent report */
parentReportActions: OnyxEntry<OnyxTypes.ReportActions>;
};

/** Route params */
route: PropTypes.shape({
params: PropTypes.shape({
/** Report ID passed via route r/:reportID/:reportActionID */
reportID: PropTypes.string,
type FlagCommentPageNavigationProps = StackScreenProps<FlagCommentNavigatorParamList, typeof SCREENS.FLAG_COMMENT_ROOT>;

/** ReportActionID passed via route r/:reportID/:reportActionID */
reportActionID: PropTypes.string,
}),
}).isRequired,
type FlagCommentPageProps = FlagCommentPageNavigationProps & WithReportAndReportActionOrNotFoundProps & FlagCommentPageWithOnyxProps;

...withLocalizePropTypes,
type Severity = ValueOf<typeof CONST.MODERATION>;

/* Onyx Props */
/** All the report actions from the parent report */
parentReportActions: PropTypes.objectOf(PropTypes.shape(reportActionPropTypes)),
type SeverityItem = {
severity: Severity;
name: string;
icon: React.FC<SvgProps>;
description: string;
furtherDetails: string;
furtherDetailsIcon: React.FC<SvgProps>;
};

const defaultProps = {
reportActions: {},
parentReportActions: {},
report: {},
};
type SeverityItemList = SeverityItem[];

/**
* Get the reportID for the associated chatReport
*
* @param {Object} route
* @param {Object} route.params
* @param {String} route.params.reportID
* @returns {String}
*/
function getReportID(route) {
function getReportID(route: FlagCommentPageNavigationProps['route']) {
return route.params.reportID.toString();
}

function FlagCommentPage(props) {
function FlagCommentPage({parentReportActions, route, report, reportActions}: FlagCommentPageProps) {
const styles = useThemeStyles();
const severities = [
const {translate} = useLocalize();

const severities: SeverityItemList = [
{
severity: CONST.MODERATION.FLAG_SEVERITY_SPAM,
name: props.translate('moderation.spam'),
name: translate('moderation.spam'),
icon: Expensicons.FlagLevelOne,
description: props.translate('moderation.spamDescription'),
furtherDetails: props.translate('moderation.levelOneResult'),
description: translate('moderation.spamDescription'),
furtherDetails: translate('moderation.levelOneResult'),
furtherDetailsIcon: Expensicons.FlagLevelOne,
},
{
severity: CONST.MODERATION.FLAG_SEVERITY_INCONSIDERATE,
name: props.translate('moderation.inconsiderate'),
name: translate('moderation.inconsiderate'),
icon: Expensicons.FlagLevelOne,
description: props.translate('moderation.inconsiderateDescription'),
furtherDetails: props.translate('moderation.levelOneResult'),
description: translate('moderation.inconsiderateDescription'),
furtherDetails: translate('moderation.levelOneResult'),
furtherDetailsIcon: Expensicons.FlagLevelOne,
},
{
severity: CONST.MODERATION.FLAG_SEVERITY_INTIMIDATION,
name: props.translate('moderation.intimidation'),
name: translate('moderation.intimidation'),
icon: Expensicons.FlagLevelTwo,
description: props.translate('moderation.intimidationDescription'),
furtherDetails: props.translate('moderation.levelTwoResult'),
description: translate('moderation.intimidationDescription'),
furtherDetails: translate('moderation.levelTwoResult'),
furtherDetailsIcon: Expensicons.FlagLevelTwo,
},
{
severity: CONST.MODERATION.FLAG_SEVERITY_BULLYING,
name: props.translate('moderation.bullying'),
name: translate('moderation.bullying'),
icon: Expensicons.FlagLevelTwo,
description: props.translate('moderation.bullyingDescription'),
furtherDetails: props.translate('moderation.levelTwoResult'),
description: translate('moderation.bullyingDescription'),
furtherDetails: translate('moderation.levelTwoResult'),
furtherDetailsIcon: Expensicons.FlagLevelTwo,
},
{
severity: CONST.MODERATION.FLAG_SEVERITY_HARASSMENT,
name: props.translate('moderation.harassment'),
name: translate('moderation.harassment'),
icon: Expensicons.FlagLevelThree,
description: props.translate('moderation.harassmentDescription'),
furtherDetails: props.translate('moderation.levelThreeResult'),
description: translate('moderation.harassmentDescription'),
furtherDetails: translate('moderation.levelThreeResult'),
furtherDetailsIcon: Expensicons.FlagLevelThree,
},
{
severity: CONST.MODERATION.FLAG_SEVERITY_ASSAULT,
name: props.translate('moderation.assault'),
name: translate('moderation.assault'),
icon: Expensicons.FlagLevelThree,
description: props.translate('moderation.assaultDescription'),
furtherDetails: props.translate('moderation.levelThreeResult'),
description: translate('moderation.assaultDescription'),
furtherDetails: translate('moderation.levelThreeResult'),
furtherDetailsIcon: Expensicons.FlagLevelThree,
},
];

const getActionToFlag = useCallback(() => {
let reportAction = props.reportActions[`${props.route.params.reportActionID.toString()}`];
const getActionToFlag = useCallback((): OnyxTypes.ReportAction | null => {
let reportAction = reportActions?.[`${route.params.reportActionID.toString()}`];

// Handle threads if needed
if (reportAction === undefined || reportAction.reportActionID === undefined) {
reportAction = props.parentReportActions[props.report.parentReportActionID] || {};
if (reportAction?.reportActionID === undefined) {
reportAction = parentReportActions?.[report?.parentReportActionID ?? ''];
}

if (!reportAction) {
return null;
}

return reportAction;
}, [props.report, props.reportActions, props.route.params.reportActionID, props.parentReportActions]);
}, [report, reportActions, route.params.reportActionID, parentReportActions]);

const flagComment = (severity) => {
let reportID = getReportID(props.route);
const flagComment = (severity: Severity) => {
let reportID: string | undefined = getReportID(route);
const reportAction = getActionToFlag();
const parentReportAction = props.parentReportActions[props.report.parentReportActionID] || {};
const parentReportAction = parentReportActions?.[report?.parentReportActionID ?? ''];

// Handle threads if needed
if (ReportUtils.isChatThread(props.report) && reportAction.reportActionID === parentReportAction.reportActionID) {
reportID = ReportUtils.getParentReport(props.report).reportID;
if (ReportUtils.isChatThread(report) && reportAction?.reportActionID === parentReportAction?.reportActionID) {
reportID = ReportUtils.getParentReport(report)?.reportID;
}

if (ReportUtils.canFlagReportAction(reportAction, reportID)) {
Report.flagComment(reportID, reportAction, severity);
if (reportAction && ReportUtils.canFlagReportAction(reportAction, reportID)) {
Report.flagComment(reportID ?? '', reportAction, severity);
}

Navigation.dismissModal();
};

const severityMenuItems = _.map(severities, (item, index) => (
const severityMenuItems = severities.map((item) => (
<MenuItem
key={`${item.severity}_${index}`}
key={`${item.severity}`}
shouldShowRightIcon
title={item.name}
description={item.description}
Expand All @@ -166,13 +161,13 @@ function FlagCommentPage(props) {
testID={FlagCommentPage.displayName}
>
{({safeAreaPaddingBottomStyle}) => (
<FullPageNotFoundView shouldShow={!ReportUtils.shouldShowFlagComment(getActionToFlag(), props.report)}>
<FullPageNotFoundView shouldShow={!ReportUtils.shouldShowFlagComment(getActionToFlag(), report)}>
<HeaderWithBackButton
title={props.translate('reportActionContextMenu.flagAsOffensive')}
title={translate('reportActionContextMenu.flagAsOffensive')}
shouldNavigateToTopMostReport
onBackButtonPress={() => {
Navigation.goBack();
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(props.report.reportID));
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(report?.reportID ?? ''));
}}
/>
<ScrollView
Expand All @@ -181,10 +176,10 @@ function FlagCommentPage(props) {
>
<View style={styles.pageWrapper}>
<View style={styles.settingsPageBody}>
<Text style={styles.baseFontStyle}>{props.translate('moderation.flagDescription')}</Text>
<Text style={styles.webViewStyles.baseFontStyle}>{translate('moderation.flagDescription')}</Text>
</View>
</View>
<Text style={[styles.ph5, styles.textLabelSupporting, styles.mb1]}>{props.translate('moderation.chooseAReason')}</Text>
<Text style={[styles.ph5, styles.textLabelSupporting, styles.mb1]}>{translate('moderation.chooseAReason')}</Text>
{severityMenuItems}
</ScrollView>
</FullPageNotFoundView>
Expand All @@ -193,17 +188,13 @@ function FlagCommentPage(props) {
);
}

FlagCommentPage.propTypes = propTypes;
FlagCommentPage.defaultProps = defaultProps;
FlagCommentPage.displayName = 'FlagCommentPage';

export default compose(
withLocalize,
withReportAndReportActionOrNotFound,
withOnyx({
export default withReportAndReportActionOrNotFound(
withOnyx<FlagCommentPageProps, FlagCommentPageWithOnyxProps>({
parentReportActions: {
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.parentReportID || report.reportID}`,
key: ({report}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID ?? report?.reportID}`,
canEvict: false,
},
}),
)(FlagCommentPage);
})(FlagCommentPage),
);
15 changes: 9 additions & 6 deletions src/pages/home/report/withReportAndReportActionOrNotFound.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable rulesdir/no-negated-variables */
import type {RouteProp} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
import React, {useCallback, useEffect} from 'react';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
Expand All @@ -9,11 +9,13 @@ import withWindowDimensions from '@components/withWindowDimensions';
import type {WindowDimensionsProps} from '@components/withWindowDimensions/types';
import compose from '@libs/compose';
import getComponentDisplayName from '@libs/getComponentDisplayName';
import type {FlagCommentNavigatorParamList} from '@libs/Navigation/types';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import * as Report from '@userActions/Report';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

Expand All @@ -37,12 +39,11 @@ type OnyxProps = {
isLoadingReportData: OnyxEntry<boolean>;
};

type ComponentProps = OnyxProps &
WindowDimensionsProps & {
route: RouteProp<{params: {reportID: string; reportActionID: string}}>;
};
type WithReportAndReportActionOrNotFoundProps = OnyxProps & WindowDimensionsProps & StackScreenProps<FlagCommentNavigatorParamList, typeof SCREENS.FLAG_COMMENT_ROOT>;

export default function <TProps extends ComponentProps, TRef>(WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>): ComponentType<TProps & RefAttributes<TRef>> {
export default function <TProps extends WithReportAndReportActionOrNotFoundProps, TRef>(
WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>,
): ComponentType<TProps & RefAttributes<TRef>> {
function WithReportOrNotFound(props: TProps, ref: ForwardedRef<TRef>) {
const getReportAction = useCallback(() => {
let reportAction: OnyxTypes.ReportAction | Record<string, never> | undefined = props.reportActions?.[`${props.route.params.reportActionID}`];
Expand Down Expand Up @@ -118,3 +119,5 @@ export default function <TProps extends ComponentProps, TRef>(WrappedComponent:
withWindowDimensions,
)(React.forwardRef(WithReportOrNotFound));
}

export type {WithReportAndReportActionOrNotFoundProps};