Merge branch 'master' of github.com:coralproject/talk into whizard-whitelist

This commit is contained in:
Belen Curcio
2017-02-27 17:19:54 -03:00
27 changed files with 347 additions and 110 deletions
+40 -9
View File
@@ -1,18 +1,49 @@
import React from 'react';
import {Navigation, Drawer} from 'react-mdl';
import {Link} from 'react-router';
import styles from './Header.css';
import {IndexLink, Link} from 'react-router';
import styles from './Drawer.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../../translations.json';
export default () => (
<Drawer>
<Navigation>
<Link className={styles.navLink} to="/admin">{lang.t('configure.moderate')}</Link>
<Link className={styles.navLink} to="/admin/community">{lang.t('configure.community')}</Link>
<Link className={styles.navLink} to="/admin/configure">{lang.t('configure.configure')}</Link>
</Navigation>
export default ({handleLogout, restricted = false}) => (
<Drawer className={styles.header}>
{ !restricted ?
<div>
<Navigation className={styles.nav}>
<IndexLink
className={styles.navLink}
to="/admin/dashboard"
activeClassName={styles.active}>
{lang.t('configure.dashboard')}
</IndexLink>
<Link
className={styles.navLink}
to="/admin/moderate"
activeClassName={styles.active}>
{lang.t('configure.moderate')}
</Link>
<Link className={styles.navLink}
to="/admin/streams"
activeClassName={styles.active}>
{lang.t('configure.streams')}
</Link>
<Link className={styles.navLink}
to="/admin/community"
activeClassName={styles.active}>
{lang.t('configure.community')}
</Link>
<Link
className={styles.navLink}
to="/admin/configure"
activeClassName={styles.active}>
{lang.t('configure.configure')}
</Link>
<a onClick={handleLogout}>Sign Out</a>
<span>{`v${process.env.VERSION}`}</span>
</Navigation>
</div> : null }
</Drawer>
);
const lang = new I18n(translations);
@@ -1,7 +1,25 @@
@custom-media --table-viewport (max-width: 1024px);
@media (--table-viewport) {
.logo {
margin-left: 58px;
h1 {
background: #696969;
}
span {
color: #fcfcfc;
}
}
.nav {
display: none;
}
}
.header {
background-color: transparent;
box-shadow: none;
min-height: 58px;
display: block;
}
.header > div {
@@ -8,7 +8,7 @@ import {Logo} from './Logo';
export default ({handleLogout, restricted = false}) => (
<Header className={styles.header}>
<Logo />
<Logo className={styles.logo} />
{
!restricted ?
<div>
@@ -6,8 +6,8 @@ import styles from './Layout.css';
export const Layout = ({children, ...props}) => (
<LayoutMDL fixedDrawer>
<Header {...props}/>
<Drawer />
<Header {...props} />
<Drawer {...props} />
<div className={styles.layout} >
{children}
</div>
+2 -2
View File
@@ -2,8 +2,8 @@ import React from 'react';
import styles from './Logo.css';
import {CoralLogo} from 'coral-ui';
export const Logo = () => (
<div className={styles.logo}>
export const Logo = ({className = ''}) => (
<div className={`${styles.logo} ${className}`}>
<h1>
<CoralLogo className={styles.base} />
<span>Talk</span>
@@ -43,7 +43,7 @@ class ModerationContainer extends Component {
}
render () {
const {data, moderation, settings, assets, ...props} = this.props;
const {data, moderation, settings, assets, modQueueResort, ...props} = this.props;
const providedAssetId = this.props.params.id;
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
@@ -73,6 +73,7 @@ class ModerationContainer extends Component {
premodCount={data.premodCount}
rejectedCount={data.rejectedCount}
flaggedCount={data.flaggedCount}
modQueueResort={modQueueResort}
/>
<ModerationQueue
data={data}
@@ -1,42 +1,64 @@
import React, {PropTypes} from 'react';
import React, {PropTypes, Component} from 'react';
import CommentCount from './CommentCount';
import styles from './styles.css';
import {SelectField, Option} from 'react-mdl-selectfield';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations.json';
import {Link} from 'react-router';
const lang = new I18n(translations);
const ModerationMenu = ({asset, premodCount, rejectedCount, flaggedCount}) => {
const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
return (
<div className='mdl-tabs'>
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
<div>
<Link to={premodPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.premod')} <CommentCount count={premodCount} />
</Link>
<Link to={rejectPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.rejected')} <CommentCount count={rejectedCount} />
</Link>
<Link to={flagPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.flagged')} <CommentCount count={flaggedCount} />
</Link>
class ModerationMenu extends Component {
state = {
sort: 'REVERSE_CHRONOLOGICAL',
}
static propTypes = {
premodCount: PropTypes.number.isRequired,
rejectedCount: PropTypes.number.isRequired,
flaggedCount: PropTypes.number.isRequired,
asset: PropTypes.shape({
id: PropTypes.string
})
}
selectSort = (sort) => {
this.setState({sort});
this.props.modQueueResort(sort);
}
render() {
const {asset, premodCount, rejectedCount, flaggedCount} = this.props;
const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
return (
<div className='mdl-tabs'>
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
<div className={styles.tabBarPadding}/>
<div>
<Link to={premodPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.premod')} <CommentCount count={premodCount} />
</Link>
<Link to={rejectPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.rejected')} <CommentCount count={rejectedCount} />
</Link>
<Link to={flagPath} className={`mdl-tabs__tab ${styles.tab}`} activeClassName={styles.active}>
{lang.t('modqueue.flagged')} <CommentCount count={flaggedCount} />
</Link>
</div>
<SelectField
className={styles.selectField}
label='Sort'
value={this.state.sort}
onChange={sort => this.selectSort(sort)}>
<Option value={'CHRONOLOGICAL'}>Newest First</Option>
<Option value={'REVERSE_CHRONOLOGICAL'}>Oldest First</Option>
</SelectField>
</div>
</div>
</div>
);
};
ModerationMenu.propTypes = {
premodCount: PropTypes.number.isRequired,
rejectedCount: PropTypes.number.isRequired,
flaggedCount: PropTypes.number.isRequired,
asset: PropTypes.shape({
id: PropTypes.string
})
};
);
}
}
export default ModerationMenu;
@@ -8,6 +8,12 @@
.tabBar {
background-color: rgba(44, 44, 44, 0.89);
z-index: 5;
display: flex;
justify-content: space-between;
}
.tabBarPadding {
width: 150px;
}
.tab {
@@ -334,3 +340,41 @@ span {
font-weight: 500;
}
}
.selectField {
position: relative;
width: 140px;
height: 36px;
top: 5px;
margin-right: 10px;
background: #FFF;
padding: 10px 15px;
box-sizing: border-box;
border-radius: 2px;
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
> div {
padding: 0;
}
i {
position: absolute;
top: 7px;
right: 7px;
}
input {
padding: 0;
font-size: 13px;
letter-spacing: 0.7px;
font-weight: 400;
}
label {
top: -4px;
}
&:hover {
cursor: pointer;
}
}
@@ -23,8 +23,24 @@ export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
options: ({params: {id = null}}) => {
return {
variables: {
asset_id: id
asset_id: id,
sort: 'REVERSE_CHRONOLOGICAL'
}
};
}
},
props: ({ownProps: {params: {id = null}}, data}) => ({
data,
modQueueResort: modQueueResort(id, data.fetchMore)
})
});
export const modQueueResort = (id, fetchMore) => (sort) => {
return fetchMore({
query: MOD_QUEUE_QUERY,
variables: {
asset_id: id,
sort
},
updateQuery: (oldData, {fetchMoreResult:{data}}) => data
});
};
@@ -1,16 +1,18 @@
#import "../fragments/commentView.graphql"
query ModQueue ($asset_id: ID) {
query ModQueue ($asset_id: ID, $sort: SORT_ORDER) {
premod: comments(query: {
statuses: [PREMOD],
asset_id: $asset_id
asset_id: $asset_id,
sort: $sort
}) {
...commentView
}
flagged: comments(query: {
action_type: FLAG,
asset_id: $asset_id,
statuses: [NONE, PREMOD]
statuses: [NONE, PREMOD],
sort: $sort
}) {
...commentView
action_summaries {
@@ -22,7 +24,8 @@ query ModQueue ($asset_id: ID) {
}
rejected: comments(query: {
statuses: [REJECTED],
asset_id: $asset_id
asset_id: $asset_id,
sort: $sort
}) {
...commentView
}
+3
View File
@@ -7,6 +7,9 @@ import store from './services/store';
import App from './components/App';
import 'react-mdl/extra/material.css';
import 'react-mdl/extra/material.js';
render(
<ApolloProvider client={client} store={store}>
<App />
+4 -1
View File
@@ -90,6 +90,7 @@ class Comment extends React.Component {
showSignInDialog,
postLike,
postFlag,
postDontAgree,
loadMore,
setActiveReplyBox,
activeReplyBox,
@@ -98,6 +99,7 @@ class Comment extends React.Component {
const like = getActionSummary('LikeActionSummary', comment);
const flag = getActionSummary('FlagActionSummary', comment);
const dontagree = getActionSummary('DontAgreeActionSummary', comment);
return (
<div
@@ -129,10 +131,11 @@ class Comment extends React.Component {
<div className="commentActionsRight">
<PermalinkButton articleURL={asset.url} commentId={comment.id} />
<FlagComment
flag={flag}
flag={flag && flag.current_user ? flag : dontagree}
id={comment.id}
author_id={comment.user.id}
postFlag={postFlag}
postDontAgree={postDontAgree}
deleteAction={deleteAction}
showSignInDialog={showSignInDialog}
currentUser={currentUser} />
+3 -1
View File
@@ -13,7 +13,7 @@ const {addNotification, clearNotification} = notificationActions;
const {fetchAssetSuccess} = assetActions;
import {queryStream} from 'coral-framework/graphql/queries';
import {postComment, postFlag, postLike, deleteAction} from 'coral-framework/graphql/mutations';
import {postComment, postFlag, postLike, postDontAgree, deleteAction} from 'coral-framework/graphql/mutations';
import {editName} from 'coral-framework/actions/user';
import {updateCountCache} from 'coral-framework/actions/asset';
import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework';
@@ -175,6 +175,7 @@ class Embed extends Component {
currentUser={user}
postLike={this.props.postLike}
postFlag={this.props.postFlag}
postDontAgree={this.props.postDontAgree}
getCounts={this.props.getCounts}
updateCountCache={this.props.updateCountCache}
loadMore={this.props.loadMore}
@@ -248,6 +249,7 @@ export default compose(
postComment,
postFlag,
postLike,
postDontAgree,
deleteAction,
queryStream
)(Embed);
+2
View File
@@ -60,6 +60,7 @@ class Stream extends React.Component {
addNotification,
postFlag,
postLike,
postDontAgree,
loadMore,
deleteAction,
showSignInDialog,
@@ -81,6 +82,7 @@ class Stream extends React.Component {
currentUser={currentUser}
postLike={postLike}
postFlag={postFlag}
postDontAgree={postDontAgree}
loadMore={loadMore}
deleteAction={deleteAction}
showSignInDialog={showSignInDialog}
@@ -2,6 +2,7 @@ import {graphql} from 'react-apollo';
import POST_COMMENT from './postComment.graphql';
import POST_FLAG from './postFlag.graphql';
import POST_LIKE from './postLike.graphql';
import POST_DONT_AGREE from './postDontAgree.graphql';
import DELETE_ACTION from './deleteAction.graphql';
import commentView from '../fragments/commentView.graphql';
@@ -100,6 +101,17 @@ export const postFlag = graphql(POST_FLAG, {
}}),
});
export const postDontAgree = graphql(POST_DONT_AGREE, {
props: ({mutate}) => ({
postDontAgree: (dontagree) => {
return mutate({
variables: {
dontagree
}
});
}}),
});
export const deleteAction = graphql(DELETE_ACTION, {
props: ({mutate}) => ({
deleteAction: (id) => {
@@ -0,0 +1,10 @@
mutation CreateDontAgree($dontagree: CreateDontAgreeInput!) {
createDontAgree(dontagree:$dontagree) {
dontagree {
id
}
errors {
translation_key
}
}
}
-35
View File
@@ -1,35 +0,0 @@
import React from 'react';
import FlagButton from './FlagButton';
import {I18n} from '../coral-framework';
import translations from './translations.json';
const FlagBio = (props) => <FlagButton {...props} getPopupMenu={getPopupMenu} />;
const getPopupMenu = [
() => {
return {
header: lang.t('step-2-header'),
itemType: 'USERS',
field: 'bio',
options: [
{val: 'This bio is offensive', text: lang.t('bio-offensive')},
{val: 'I don\'t like this bio', text: lang.t('no-like-bio')},
{val: 'This looks like an ad/marketing', text: lang.t('marketing')},
{val: 'other', text: lang.t('other')}
],
button: lang.t('continue'),
sets: 'reason'
};
},
() => {
return {
header: lang.t('step-3-header'),
text: lang.t('thank-you'),
button: lang.t('done'),
};
}
];
export default FlagBio;
const lang = new I18n(translations);
+20 -10
View File
@@ -38,7 +38,7 @@ class FlagButton extends Component {
}
onPopupContinue = () => {
const {postFlag, id, author_id} = this.props;
const {postFlag, postDontAgree, id, author_id} = this.props;
const {itemType, reason, step, posted, message} = this.state;
// Proceed to the next step or close the menu if we've reached the end
@@ -52,7 +52,6 @@ class FlagButton extends Component {
if (itemType && reason && !posted) {
this.setState({posted: true});
// Set the text from the "other" field if it exists.
let item_id;
switch(itemType) {
case 'COMMENTS':
@@ -63,20 +62,31 @@ class FlagButton extends Component {
break;
}
// Note: Action metadata has been temporarily removed.
if (itemType === 'COMMENTS') {
this.setState({localPost: 'temp'});
}
postFlag({
let action = {
item_id,
item_type: itemType,
reason,
reason: null,
message
}).then(({data}) => {
if (itemType === 'COMMENTS') {
this.setState({localPost: data.createFlag.flag.id});
}
});
};
if (reason === 'I don\'t agree with this comment') {
postDontAgree(action)
.then(({data}) => {
if (itemType === 'COMMENTS') {
this.setState({localPost: data.createDontAgree.dontagree.id});
}
});
} else {
postFlag({...action, reason})
.then(({data}) => {
if (itemType === 'COMMENTS') {
this.setState({localPost: data.createFlag.flag.id});
}
});
}
}
}
+1 -1
View File
@@ -20,9 +20,9 @@ const getPopupMenu = [
(itemType) => {
const options = itemType === 'COMMENTS' ?
[
{val: 'I don\'t agree with this comment', text: lang.t('no-agree-comment')},
{val: 'This comment is offensive', text: lang.t('comment-offensive')},
{val: 'This looks like an ad/marketing', text: lang.t('marketing')},
{val: 'I don\'t agree with this comment', text: lang.t('no-agree-comment')},
{val: 'other', text: lang.t('other')}
]
: [