Skip to content

Commit

Permalink
Merge pull request #3202 from mozilla/report-abuse-ui-2759
Browse files Browse the repository at this point in the history
Report add-on abuse UI
  • Loading branch information
tofumatt committed Sep 26, 2017
2 parents c4866d4 + 865800b commit b74c148
Show file tree
Hide file tree
Showing 13 changed files with 816 additions and 12 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
"react-redux-loading-bar": "2.9.2",
"react-router": "2.8.1",
"react-router-scroll": "0.4.2",
"react-textarea-autosize": "^5.1.0",
"redux": "3.7.2",
"redux-connect": "4.0.2",
"redux-logger": "3.0.6",
Expand Down
5 changes: 4 additions & 1 deletion src/amo/components/AddonMoreInfo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { compose } from 'redux';

import Link from 'amo/components/Link';
import ReportAbuseButton from 'amo/components/ReportAbuseButton';
import translate from 'core/i18n/translate';
import { trimAndAddProtocolToUrl } from 'core/utils';
import Card from 'ui/components/Card';
Expand Down Expand Up @@ -151,14 +152,16 @@ export class AddonMoreInfoBase extends React.Component {
}

render() {
const { i18n } = this.props;
const { addon, i18n } = this.props;

return (
<Card
className="AddonMoreInfo"
header={i18n.gettext('More information')}
>
{this.listContent()}

<ReportAbuseButton addon={addon} />
</Card>
);
}
Expand Down
221 changes: 221 additions & 0 deletions src/amo/components/ReportAbuseButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import classNames from 'classnames';
import { oneLine } from 'common-tags';
import React from 'react';
import { connect } from 'react-redux';
import Textarea from 'react-textarea-autosize';
import { compose } from 'redux';

import { withErrorHandler } from 'core/errorHandler';
import type { ErrorHandlerType } from 'core/errorHandler';
import translate from 'core/i18n/translate';
import log from 'core/logger';
import {
disableAbuseButtonUI,
enableAbuseButtonUI,
hideAddonAbuseReportUI,
sendAddonAbuseReport,
showAddonAbuseReportUI,
} from 'core/reducers/abuse';
import { sanitizeHTML } from 'core/utils';
import Button from 'ui/components/Button';

import './styles.scss';


type PropTypes = {
abuseReport: {|
message: string,
reporter: Object | null,
|},
addon: Object | null,
dispatch: Function,
errorHandler: ErrorHandlerType,
loading: bool,
i18n: Object,
};

export class ReportAbuseButtonBase extends React.Component {
dismissReportUI = (event) => {
event.preventDefault();

const { addon, dispatch, loading } = this.props;

if (loading) {
log.debug(
"Ignoring dismiss click because we're submitting the abuse report");
return;
}

dispatch(hideAddonAbuseReportUI({ addon }));
}

sendReport = (event) => {
event.preventDefault();

// The button isn't clickable if there is no content, but just in case:
// we verify there's a message to send.
if (!this.textarea.value.length) {
log.debug(oneLine`User managed to click submit button while textarea
was empty. Ignoring this onClick/sendReport event.`);
return;
}

const { addon, dispatch, errorHandler } = this.props;

dispatch(sendAddonAbuseReport({
addonSlug: addon.slug,
errorHandlerId: errorHandler.id,
message: this.textarea.value,
}));
}

showReportUI = (event) => {
event.preventDefault();

const { addon, dispatch } = this.props;

dispatch(showAddonAbuseReportUI({ addon }));
this.textarea.focus();
}

textareaChange = () => {
const { abuseReport, addon, dispatch } = this.props;

// Don't dispatch the UI update if the button is already visible.
// We also test for `value.trim()` so the user can't submit an
// empty report full of spaces.
if (this.textarea.value.trim().length && !abuseReport.buttonEnabled) {
dispatch(enableAbuseButtonUI({ addon }));
} else if (!this.textarea.value.trim().length) {
dispatch(disableAbuseButtonUI({ addon }));
}
}

props: PropTypes;

render() {
const { abuseReport, addon, i18n, loading } = this.props;

if (!addon) {
return null;
}

if (abuseReport && abuseReport.message) {
return (
<div className="ReportAbuseButton ReportAbuseButton--report-sent">
<h3 className="ReportAbuseButton-header">
{i18n.gettext('You reported this add-on for abuse')}
</h3>

<p className="ReportAbuseButton-first-paragraph">
{i18n.gettext(
`We have received your report. Thanks for letting us know about
your concerns with this add-on.`
)}
</p>

<p>
{i18n.gettext(
`We can't respond to every abuse report but we'll look into
this issue.`
)}
</p>
</div>
);
}

const sendButtonIsDisabled = loading || !abuseReport.buttonEnabled;

const prefaceText = i18n.sprintf(i18n.gettext(
`If you think this add-on violates
%(linkTagStart)sMozilla's add-on policies%(linkTagEnd)s or has
security or privacy issues, please report these issues to Mozilla using
this form.`
), {
linkTagStart: '<a href="https://developer.mozilla.org/en-US/Add-ons/AMO/Policy">',
linkTagEnd: '</a>',
});

/* eslint-disable react/no-danger */
return (
<div
className={classNames('ReportAbuseButton', {
'ReportAbuseButton--is-expanded': abuseReport.uiVisible,
})}
>
<div className="ReportAbuseButton--preview">
<Button
className="ReportAbuseButton-show-more Button--report"
onClick={this.showReportUI}
>
{i18n.gettext('Report this add-on for abuse')}
</Button>
</div>

<div className="ReportAbuseButton--expanded">
<h3 className="ReportAbuseButton-header">
{i18n.gettext('Report this add-on for abuse')}
</h3>

<p
className="ReportAbuseButton-first-paragraph"
dangerouslySetInnerHTML={sanitizeHTML(prefaceText, ['a'])}
/>

<p>{i18n.gettext(
`Please don't use this form to report bugs or request add-on
features; this report will be sent to Mozilla and not to the
add-on developer.`
)}</p>
<Textarea
className="ReportAbuseButton-textarea"
disabled={loading}
inputRef={(ref) => { this.textarea = ref; }}
onChange={this.textareaChange}
placeholder={i18n.gettext(
'Explain how this add-on is violating our policies.'
)}
/>

<div className="ReportAbuseButton-buttons">
<a
className={classNames('ReportAbuseButton-dismiss-report', {
'ReportAbuseButton-dismiss-report--disabled': loading,
})}
href="#cancel"
onClick={this.dismissReportUI}
>
{i18n.gettext('Dismiss')}
</a>
<Button
className="ReportAbuseButton-send-report Button--report Button--small"
disabled={sendButtonIsDisabled}
onClick={this.sendReport}
>
{loading ?
i18n.gettext('Sending abuse report') :
i18n.gettext('Send abuse report')}
</Button>
</div>
</div>
</div>
);
/* eslint-enable react/no-danger */
}
}

export const mapStateToProps = (state, ownProps) => {
const addon = ownProps.addon;

return {
abuseReport: addon && state.abuse.bySlug[addon.slug] ?
state.abuse.bySlug[addon.slug] : {},
loading: state.abuse.loading,
};
};

export default compose(
connect(mapStateToProps),
translate(),
withErrorHandler({ name: 'ReportAbuseButton' }),
)(ReportAbuseButtonBase);
62 changes: 62 additions & 0 deletions src/amo/components/ReportAbuseButton/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@import "~ui/css/vars";

.ReportAbuseButton {
margin: 10px auto;
}

.ReportAbuseButton-header {
margin-bottom: 0;
}

.ReportAbuseButton-first-paragraph {
margin-top: 6px;
}

.ReportAbuseButton--preview {
display: block;

.ReportAbuseButton--is-expanded & {
display: none;
}
}

.ReportAbuseButton--expanded {
display: none;

.ReportAbuseButton--is-expanded & {
display: block;
}
}

.ReportAbuseButton-show-more {
width: 100%;
}

.ReportAbuseButton-textarea {
font-size: $font-size-m-smaller;
line-height: 1.4;
margin: 5px auto;
// This is the height of two lines of text. Often the placeholder text
// will span two lines–because this element will grow/shrink based on
// the contents of the text inside it, we set a minimum height so it
// won't shrink when the two-line placeholder is replaced with a single
// character when the user starts typing.
min-height: 51px;
padding: 5px;
resize: none;
width: 100%;
}

.ReportAbuseButton-buttons {
display: flex;
justify-content: space-between;
}

.ReportAbuseButton-dismiss-report {
align-self: center;
}

.ReportAbuseButton-dismiss-report--disabled:link {
color: $neutral-base-color;
cursor: not-allowed;
}
2 changes: 1 addition & 1 deletion src/core/api/abuse.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function reportAddon(
auth: true,
endpoint: 'abuse/report/addon',
method: 'POST',
params: { addon: addonSlug, message },
body: { addon: addonSlug, message },
state: api,
});
}
1 change: 1 addition & 0 deletions src/core/css/inc/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ $default-arrow-margin: 3px;
background: $background;
border: 1px solid $border-color;
border-radius: $border-radius;
cursor: pointer;
display: inline-block;
margin: 0;
min-height: 39px;
Expand Down

0 comments on commit b74c148

Please sign in to comment.