Refactor ComemntDetail and implement ToxicDetail

This commit is contained in:
Chi Vinh Le
2017-09-27 22:50:45 +07:00
parent ef49d9a3d2
commit 219399a231
14 changed files with 235 additions and 102 deletions
@@ -0,0 +1,34 @@
.root {
margin-left: 16px;
}
.headerContainer {
display: flex;
justify-content: flex-start;
align-items: center;
}
.header {
vertical-align: middle;
margin: 0;
font-weight: 500;
display: inline-block;
font-size: 12px;
line-height: 12px;
margin-right: 7px;
}
.info {
font-size: 12px;
}
.details {
padding: 0 20px 16px;
font-size: 12px;
}
.icon {
vertical-align: middle;
font-size: 12px;
margin-right: 7px;
}
@@ -0,0 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
import styles from './CommentDetail.css';
import {Icon} from 'coral-ui';
const CommentDetail = ({icon, header, info, children, className}) => {
return (
<div className={cn(className, styles.root)}>
<div className={styles.headerContainer}>
{icon && <Icon className={styles.icon} name={icon}/>}
<h3 className={styles.header}>{header}:</h3>
<div className={styles.info}>{info}</div>
</div>
{children &&
<div className={styles.details}>
{children}
</div>
}
</div>
);
};
CommentDetail.propTypes = {
className: PropTypes.string,
header: PropTypes.node,
icon: PropTypes.string,
info: PropTypes.node,
children: PropTypes.node,
};
export default CommentDetail;
+35 -58
View File
@@ -1,56 +1,42 @@
.flagBox {
border-top: 1px solid rgba(66, 66, 66, 0.12);
margin-top: 10px;
.container {
padding: 0 14px;
}
padding-top: 10px;
}
.detail {
padding: 0 20px 16px;
ul {
padding: 0;
list-style: none;
font-size: 12px;
font-weight: 500;
}
}
.info {
vertical-align: middle;
list-style: none;
display: inline-block;
padding: 0;
font-size: 12px;
}
.header {
position: relative;
.moreDetail {
float: right;
font-size: 12px;
font-weight: 500;
margin-right: 10px;
margin-top: 8px;
color: black;
.detail {
padding: 0;
list-style: none;
font-size: 12px;
font-weight: 500;
}
&:hover {
opacity: 0.9;
cursor: pointer;
}
}
i {
vertical-align: middle;
font-size: 12px;
}
ul {
vertical-align: middle;
list-style: none;
display: inline-block;
padding: 0;
margin-left: 10px;
font-size: 12px;
}
}
.subDetail {
padding: 0;
list-style: none;
font-size: 12px;
font-weight: normal;
color: #888;
}
h3 {
vertical-align: middle;
margin: 0;
font-weight: 500;
display: inline-block;
margin-left: 7px;
font-size: 12px;
.moreDetail {
position: absolute;
font-size: 12px;
font-weight: 500;
color: black;
right: 16px;
&:hover {
opacity: 0.9;
cursor: pointer;
}
}
@@ -59,15 +45,6 @@
margin-right: 10px;
}
.subDetail {
font-weight: normal;
color: #888;
span {
color: black;
}
}
.username {
color: #393B44;
text-decoration: none;
@@ -77,7 +54,7 @@
border-radius: 2px;
margin-left: -5px;
transition: background-color 200ms ease;
&:hover {
background-color: #E0E0E0;
}
&:hover {
background-color: #E0E0E0;
}
}
+31 -34
View File
@@ -1,8 +1,8 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Icon} from 'coral-ui';
import styles from './FlagBox.css';
import t from 'coral-framework/services/i18n';
import CommentDetail from './CommentDetail';
const shortReasons = {
'This comment is offensive': t('modqueue.offensive'),
@@ -39,45 +39,42 @@ class FlagBox extends Component {
return (
<div className={styles.flagBox}>
<div className={styles.container}>
<div className={styles.header}>
<Icon name='flag'/><h3>{t('community.flags')} ({actionSummaries.length}):</h3>
<ul>
<a onClick={this.toggleDetail} className={styles.moreDetail}>{showDetail ? t('modqueue.less_detail') : t('modqueue.more_detail')}</a>
<CommentDetail
icon={'flag'}
header={`${t('community.flags')} (${actionSummaries.length})`}
info={
<ul className={styles.info}>
{actionSummaries.map((action, i) =>
<li key={i} className={styles.lessDetail}> {this.reasonMap(action.reason)} (<strong>{action.count}</strong>)</li>
)}
</ul>
<a onClick={this.toggleDetail} className={styles.moreDetail}>{showDetail ? t('modqueue.less_detail') : t('modqueue.more_detail')}</a>
</div>
}>
{showDetail && (
<div className={styles.detail}>
<ul>
{actionSummaries.map((summary, i) => {
const actionList = actions.filter((a) => a.reason === summary.reason);
return (
<li key={i}>
{this.reasonMap(summary.reason)} (<strong>{summary.count}</strong>)
<ul>
{actionList.map((action, j) =>
<li key={`${i}_${j}`} className={styles.subDetail}>
{action.user &&
<a className={styles.username} onClick={() => viewUserDetail(action.user.id)}>
{action.user.username}
</a>
}
{action.message}
</li>
)}
</ul>
</li>
);
})}
</ul>
</div>
<ul className={styles.detail}>
{actionSummaries.map((summary, i) => {
const actionList = actions.filter((a) => a.reason === summary.reason);
return (
<li key={i}>
{this.reasonMap(summary.reason)} (<strong>{summary.count}</strong>)
<ul>
{actionList.map((action, j) =>
<li key={`${i}_${j}`} className={styles.subDetail}>
{action.user &&
<a className={styles.username} onClick={() => viewUserDetail(action.user.id)}>
{action.user.username}
</a>
}
{action.message}
</li>
)}
</ul>
</li>
);
})}
</ul>
)}
</div>
</CommentDetail>
</div>
);
}
@@ -185,11 +185,6 @@ class Comment extends React.Component {
</div>
</CommentAnimatedEdit>
</div>
<Slot
fill="adminCommentDetailArea"
data={data}
queryData={queryData}
/>
{flagActions && flagActions.length
? <FlagBox
actions={flagActions}
@@ -197,6 +192,11 @@ class Comment extends React.Component {
viewUserDetail={viewUserDetail}
/>
: null}
<Slot
fill="adminCommentDetailArea"
data={data}
queryData={queryData}
/>
</li>
);
}
@@ -0,0 +1,11 @@
.info {
background-color: rgba(44, 44, 44, 0.89);
color: white;
padding: 2px 4px;
font-size: 0.8em;
margin-left: 6px;
}
.toxic {
background-color: #d03235;
}
@@ -0,0 +1,46 @@
import React from 'react';
import CommentDetail from 'coral-admin/src/components/CommentDetail';
import {isToxic} from '../utils';
import styles from './ToxicDetail.css';
import cn from 'classnames';
import PropTypes from 'prop-types';
const getInfo = (toxicity, actions) => {
const toxic = isToxic(actions);
let text = 'Unlikely';
if (toxicity > 0.8) {
text = 'Highly Likely';
}
else if (toxicity >= 0.5) {
text = 'Possibly';
}
else if (toxicity >= 0.7) {
text = 'Likely';
}
return (
<div>
{text}
<span className={cn(styles.info, {[styles.toxic]: toxic})}>
{Math.round(toxicity * 100)}%
</span>
</div>
);
};
const ToxicLabel = ({comment: {actions, toxicity}}) => (
<CommentDetail
icon={'add_box'}
header={'Toxic Comment'}
info={getInfo(toxicity, actions)}
/>
);
ToxicLabel.propTypes = {
comment: PropTypes.shape({
actions: PropTypes.array,
toxicity: PropTypes.toxicity,
}),
};
export default ToxicLabel;
@@ -0,0 +1,21 @@
import {compose, gql} from 'react-apollo';
import {withFragments, excludeIf} from 'plugin-api/beta/client/hocs';
import ToxicDetail from '../components/ToxicDetail';
const enhance = compose(
withFragments({
comment: gql`
fragment TalkToxicComments_ToxicDetail_Comment on Comment {
toxicity
actions {
__typename
... on FlagAction {
reason
}
}
}`,
}),
excludeIf(({comment: {toxicity}}) => toxicity === null),
);
export default enhance(ToxicDetail);
@@ -1,15 +1,12 @@
import {compose, gql} from 'react-apollo';
import {withFragments, excludeIf} from 'plugin-api/beta/client/hocs';
import ToxicLabel from '../components/ToxicLabel';
function isToxic(actions) {
return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TOXIC_COMMENT');
}
import {isToxic} from '../utils';
const enhance = compose(
withFragments({
comment: gql`
fragment TalkToxicComments_Comment on Comment {
fragment TalkToxicComments_ToxicLabel_Comment on Comment {
actions {
__typename
... on FlagAction {
@@ -1,11 +1,13 @@
import translations from './translations.yml';
import CheckToxicityHook from './containers/CheckToxicityHook';
import ToxicLabel from './containers/ToxicLabel';
import ToxicDetail from './containers/ToxicDetail';
export default {
translations,
slots: {
commentInputDetailArea: [CheckToxicityHook],
adminCommentLabels: [ToxicLabel],
adminCommentDetailArea: [ToxicDetail],
},
};
@@ -0,0 +1,4 @@
export function isToxic(actions) {
return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TOXIC_COMMENT');
}
@@ -1,8 +1,10 @@
const {readFileSync} = require('fs');
const path = require('path');
const hooks = require('./server/hooks');
const resolvers = require('./server/resolvers');
module.exports = {
typeDefs: readFileSync(path.join(__dirname, 'server/typeDefs.graphql'), 'utf8'),
hooks,
resolvers,
};
@@ -0,0 +1,7 @@
const get = require('lodash/get');
module.exports = {
Comment: {
toxicity: (comment) => get(comment, 'metadata.perspective.SEVERE_TOXICITY.summaryScore'),
}
};
@@ -5,3 +5,6 @@ input CreateCommentInput {
checkToxicity: Boolean
}
type Comment {
toxicity: Float
}