src/amo/pages/CollectionFeedback/index.js (168 lines of code) (raw):

/* @flow */ import * as React from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { Helmet } from 'react-helmet'; import invariant from 'invariant'; import FeedbackForm, { CATEGORY_FEEDBACK_SPAM, CATEGORY_HATEFUL_VIOLENT_DECEPTIVE, CATEGORY_ILLEGAL, CATEGORY_SOMETHING_ELSE, } from 'amo/components/FeedbackForm'; import LoadingText from 'amo/components/LoadingText'; import Card from 'amo/components/Card'; import log from 'amo/logger'; import translate from 'amo/i18n/translate'; import NotFoundPage from 'amo/pages/ErrorPages/NotFoundPage'; import Page from 'amo/components/Page'; import { fetchCurrentCollection, getCurrentCollection, } from 'amo/reducers/collections'; import { sendCollectionAbuseReport } from 'amo/reducers/collectionAbuseReports'; import { withFixedErrorHandler } from 'amo/errorHandler'; import type { AppState } from 'amo/store'; import type { ErrorHandlerType } from 'amo/types/errorHandler'; import type { DispatchFunc } from 'amo/types/redux'; import type { ReactRouterMatchType } from 'amo/types/router'; import type { I18nType } from 'amo/types/i18n'; import type { FeedbackFormValues } from 'amo/components/FeedbackForm'; import type { CollectionType } from 'amo/reducers/collections'; import './styles.scss'; type Props = {| match: {| ...ReactRouterMatchType, params: {| authorId: number, collectionSlug: string |}, |}, |}; type PropsFromState = {| collection: CollectionType | null, isCollectionLoading: boolean, hasSubmitted: boolean, isSubmitting: boolean, |}; type InternalProps = {| ...Props, ...PropsFromState, i18n: I18nType, dispatch: DispatchFunc, errorHandler: ErrorHandlerType, |}; export class CollectionFeedbackBase extends React.Component<InternalProps> { constructor(props: InternalProps) { super(props); const { collection, dispatch, errorHandler, isCollectionLoading, match } = props; const { params } = match; if (errorHandler.hasError()) { log.warn('Not loading data because of an error.'); return; } if (!collection && !isCollectionLoading) { dispatch( fetchCurrentCollection({ errorHandlerId: errorHandler.id, userId: params.authorId, slug: params.collectionSlug, }), ); } } onFormSubmitted: (values: FeedbackFormValues) => void = (values) => { const { dispatch, errorHandler, collection } = this.props; const { anonymous, email, name, text, category, illegalCategory, illegalSubcategory, } = values; invariant(collection, 'collection is required'); dispatch( sendCollectionAbuseReport({ // Only authenticate the API call when the report isn't submitted // anonymously. auth: anonymous === false, errorHandlerId: errorHandler.id, message: text, collectionId: collection.id, reason: category, reporterEmail: anonymous ? '' : email, reporterName: anonymous ? '' : name, illegalCategory, illegalSubcategory, }), ); }; render(): React.Node { const { collection, errorHandler, hasSubmitted, i18n, isSubmitting } = this.props; if ( errorHandler.hasError() && errorHandler.capturedError.responseStatusCode === 404 ) { return <NotFoundPage />; } return ( <Page> <div className="CollectionFeedback-page"> <Helmet> <title> {i18n.gettext( 'Submit feedback or report a collection to Mozilla', )} </title> <meta name="robots" content="noindex, follow" /> </Helmet> <FeedbackForm errorHandler={errorHandler} contentHeader={ <Card className="CollectionFeedback-header"> <h1 className="CollectionFeedback-header-name"> {collection ? collection.name : <LoadingText />} <span className="CollectionFeedback-header-creator"> {collection ? ( i18n.sprintf(i18n.gettext('by %(authorName)s'), { authorName: collection.authorName, }) ) : ( <LoadingText /> )} </span> </h1> <div className="CollectionFeedback-header-metadata"> <p className="CollectionFeedback-header-metadata-addons"> <span>{i18n.gettext('Add-ons')}</span> {collection ? collection.numberOfAddons : <LoadingText />} </p> <p className="CollectionFeedback-header-metadata-last-updated"> <span>{i18n.gettext('Last updated')}</span> {collection ? ( i18n.moment(collection.lastUpdatedDate).format('ll') ) : ( <LoadingText /> )} </p> </div> </Card> } abuseIsLoading={isSubmitting} abuseSubmitted={hasSubmitted} categoryHeader={i18n.gettext('Report this collection to Mozilla')} feedbackTitle={i18n.gettext( 'Send some feedback about the collection', )} reportTitle={i18n.gettext( "Report the collection because it's illegal or incompliant", )} categories={[ CATEGORY_FEEDBACK_SPAM, CATEGORY_HATEFUL_VIOLENT_DECEPTIVE, CATEGORY_ILLEGAL, CATEGORY_SOMETHING_ELSE, ]} showLocation={false} onSubmit={this.onFormSubmitted} /> </div> </Page> ); } } function mapStateToProps(state: AppState): PropsFromState { const { collections, collectionAbuseReports } = state; const { loading } = collections.current; const collection = getCurrentCollection(collections); const abuseReport = (collection && collectionAbuseReports.byCollectionId[collection.id]) || {}; return { collection, isCollectionLoading: loading, isSubmitting: abuseReport.isSubmitting || false, hasSubmitted: abuseReport.hasSubmitted || false, }; } export const extractId = (ownProps: InternalProps): string => { const { params } = ownProps.match; return `${params.authorId}-${params.collectionSlug}`; }; const CollectionFeedback: React.ComponentType<Props> = compose( translate(), connect(mapStateToProps), withFixedErrorHandler({ fileName: __filename, extractId }), )(CollectionFeedbackBase); export default CollectionFeedback;