mirror of
https://github.com/wassname/talk.git
synced 2026-07-06 03:09:49 +08:00
Merge branch 'master' into widget-auto-update
This commit is contained in:
@@ -8,7 +8,14 @@ import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
|
||||
const onBanClick = (userId, commentId, handleBanUser, rejectComment, handleClose) => (e) => {
|
||||
e.preventDefault();
|
||||
handleBanUser({userId})
|
||||
.then(handleClose)
|
||||
.then(() => rejectComment({commentId}));
|
||||
};
|
||||
|
||||
const BanUserDialog = ({open, handleClose, handleBanUser, rejectComment, user, commentId}) => (
|
||||
<Dialog
|
||||
className={styles.dialog}
|
||||
id="banuserDialog"
|
||||
@@ -29,7 +36,7 @@ const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
|
||||
<Button cStyle="cancel" className={styles.cancel} onClick={handleClose} raised>
|
||||
{lang.t('bandialog.cancel')}
|
||||
</Button>
|
||||
<Button cStyle="black" className={styles.ban} onClick={() => handleBanUser({userId: user.id})} raised>
|
||||
<Button cStyle="black" className={styles.ban} onClick={onBanClick(user.id, commentId, handleBanUser, rejectComment, handleClose)} raised>
|
||||
{lang.t('bandialog.yes_ban_user')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -21,12 +21,13 @@ import ModerationKeysModal from '../../components/ModerationKeysModal';
|
||||
|
||||
class ModerationContainer extends Component {
|
||||
state = {
|
||||
selectedIndex: 0
|
||||
selectedIndex: 0,
|
||||
sort: 'REVERSE_CHRONOLOGICAL'
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {toggleModal, singleView} = this.props;
|
||||
|
||||
|
||||
this.props.fetchSettings();
|
||||
key('s', () => singleView());
|
||||
key('shift+/', () => toggleModal(true));
|
||||
@@ -74,6 +75,11 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
selectSort = (sort) => {
|
||||
this.setState({sort});
|
||||
this.props.modQueueResort(sort);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
key.unbind('s');
|
||||
key.unbind('shift+/');
|
||||
@@ -92,7 +98,7 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data, moderation, settings, assets, modQueueResort, onClose, ...props} = this.props;
|
||||
const {data, moderation, settings, assets, onClose, ...props} = this.props;
|
||||
const providedAssetId = this.props.params.id;
|
||||
const activeTab = this.props.route.path === ':id' ? 'premod' : this.props.route.path;
|
||||
|
||||
@@ -115,6 +121,18 @@ class ModerationContainer extends Component {
|
||||
}
|
||||
|
||||
const comments = data[activeTab];
|
||||
let activeTabCount;
|
||||
switch(activeTab) {
|
||||
case 'premod':
|
||||
activeTabCount = data.premodCount;
|
||||
break;
|
||||
case 'flagged':
|
||||
activeTabCount = data.flaggedCount;
|
||||
break;
|
||||
case 'rejected':
|
||||
activeTabCount = data.rejectedCount;
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -124,7 +142,8 @@ class ModerationContainer extends Component {
|
||||
premodCount={data.premodCount}
|
||||
rejectedCount={data.rejectedCount}
|
||||
flaggedCount={data.flaggedCount}
|
||||
modQueueResort={modQueueResort}
|
||||
selectSort={this.selectSort}
|
||||
sort={this.state.sort}
|
||||
/>
|
||||
<ModerationQueue
|
||||
currentAsset={asset}
|
||||
@@ -136,12 +155,18 @@ class ModerationContainer extends Component {
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
acceptComment={props.acceptComment}
|
||||
rejectComment={props.rejectComment}
|
||||
loadMore={props.loadMore}
|
||||
assetId={providedAssetId}
|
||||
sort={this.state.sort}
|
||||
commentCount={activeTabCount}
|
||||
/>
|
||||
<BanUserDialog
|
||||
open={moderation.banDialog}
|
||||
user={moderation.user}
|
||||
commentId={moderation.commentId}
|
||||
handleClose={props.hideBanUserDialog}
|
||||
handleBanUser={props.banUser}
|
||||
rejectComment={props.rejectComment}
|
||||
/>
|
||||
<ModerationKeysModal
|
||||
open={moderation.modalOpen}
|
||||
|
||||
@@ -6,9 +6,10 @@ import EmptyCard from '../../components/EmptyCard';
|
||||
import {actionsMap} from './helpers/moderationQueueActionsMap';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from 'coral-admin/src/translations';
|
||||
import LoadMore from './components/LoadMore';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
|
||||
const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loadMore, activeTab, sort, ...props}) => {
|
||||
return (
|
||||
<div id="moderationList" className={`${styles.list} ${singleView ? styles.singleView : ''}`}>
|
||||
<ul style={{paddingLeft: 0}}>
|
||||
@@ -20,7 +21,7 @@ const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
|
||||
key={i}
|
||||
index={i}
|
||||
comment={comment}
|
||||
commentType={props.activeTab}
|
||||
commentType={activeTab}
|
||||
selected={i === selectedIndex}
|
||||
suspectWords={props.suspectWords}
|
||||
actions={actionsMap[status]}
|
||||
@@ -33,6 +34,14 @@ const ModerationQueue = ({comments, selectedIndex, singleView, ...props}) => {
|
||||
: <EmptyCard>{lang.t('modqueue.emptyqueue')}</EmptyCard>
|
||||
}
|
||||
</ul>
|
||||
<LoadMore
|
||||
comments={comments}
|
||||
loadMore={loadMore}
|
||||
sort={sort}
|
||||
tab={activeTab}
|
||||
showLoadMore={comments.length < commentCount}
|
||||
assetId={props.assetId}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import {Button} from 'coral-ui';
|
||||
import styles from './styles.css';
|
||||
|
||||
const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) =>
|
||||
<div className={styles.loadMoreContainer}>
|
||||
{
|
||||
showLoadMore && <Button
|
||||
className={styles.loadMore}
|
||||
onClick={() =>
|
||||
loadMore({
|
||||
cursor: comments[comments.length - 1].created_at,
|
||||
sort,
|
||||
tab,
|
||||
asset_id: assetId
|
||||
})}>
|
||||
Load More
|
||||
</Button>
|
||||
}
|
||||
</div>;
|
||||
|
||||
LoadMore.propTypes = {
|
||||
comments: PropTypes.array.isRequired,
|
||||
loadMore: PropTypes.func.isRequired,
|
||||
sort: PropTypes.oneOf(['CHRONOLOGICAL', 'REVERSE_CHRONOLOGICAL']).isRequired,
|
||||
tab: PropTypes.oneOf(['rejected', 'premod', 'flagged']).isRequired,
|
||||
assetId: PropTypes.string,
|
||||
showLoadMore: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
export default LoadMore;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {PropTypes, Component} from 'react';
|
||||
import React, {PropTypes} from 'react';
|
||||
import CommentCount from './CommentCount';
|
||||
import styles from './styles.css';
|
||||
import {SelectField, Option} from 'react-mdl-selectfield';
|
||||
@@ -8,57 +8,45 @@ import {Link} from 'react-router';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
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={'REVERSE_CHRONOLOGICAL'}>Newest First</Option>
|
||||
<Option value={'CHRONOLOGICAL'}>Oldest First</Option>
|
||||
</SelectField>
|
||||
const ModerationMenu = ({asset, premodCount, rejectedCount, flaggedCount, selectSort, sort}) => {
|
||||
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={sort}
|
||||
onChange={sort => selectSort(sort)}>
|
||||
<Option value={'REVERSE_CHRONOLOGICAL'}>Newest First</Option>
|
||||
<Option value={'CHRONOLOGICAL'}>Oldest First</Option>
|
||||
</SelectField>
|
||||
</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;
|
||||
|
||||
@@ -394,3 +394,23 @@ span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.loadMoreContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
};
|
||||
|
||||
.loadMore {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
max-width: 660px;
|
||||
margin-bottom: 30px;
|
||||
background-color: #2376D8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.loadMore:hover {
|
||||
background-color: #4399FF;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {graphql} from 'react-apollo';
|
||||
|
||||
import MOD_QUEUE_QUERY from './modQueueQuery.graphql';
|
||||
import MOD_QUEUE_LOAD_MORE from './loadMore.graphql';
|
||||
import METRICS from './metricsQuery.graphql';
|
||||
|
||||
export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
|
||||
@@ -14,7 +15,8 @@ export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
|
||||
},
|
||||
props: ({ownProps: {params: {id = null}}, data}) => ({
|
||||
data,
|
||||
modQueueResort: modQueueResort(id, data.fetchMore)
|
||||
modQueueResort: modQueueResort(id, data.fetchMore),
|
||||
loadMore: loadMore(data.fetchMore)
|
||||
})
|
||||
});
|
||||
|
||||
@@ -30,6 +32,40 @@ export const getMetrics = graphql(METRICS, {
|
||||
}
|
||||
});
|
||||
|
||||
export const loadMore = (fetchMore) => ({limit, cursor, sort, tab, asset_id}) => {
|
||||
let statuses;
|
||||
switch(tab) {
|
||||
case 'premod':
|
||||
statuses = ['PREMOD'];
|
||||
break;
|
||||
case 'flagged':
|
||||
statuses = ['NONE', 'PREMOD'];
|
||||
break;
|
||||
case 'rejected':
|
||||
statuses = ['REJECTED'];
|
||||
break;
|
||||
}
|
||||
return fetchMore({
|
||||
query: MOD_QUEUE_LOAD_MORE,
|
||||
variables: {
|
||||
limit,
|
||||
cursor,
|
||||
sort,
|
||||
statuses,
|
||||
asset_id
|
||||
},
|
||||
updateQuery: (oldData, {fetchMoreResult:{data:{comments}}}) => {
|
||||
return {
|
||||
...oldData,
|
||||
[tab]: [
|
||||
...oldData[tab],
|
||||
...comments
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const modQueueResort = (id, fetchMore) => (sort) => {
|
||||
return fetchMore({
|
||||
query: MOD_QUEUE_QUERY,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#import "../fragments/commentView.graphql"
|
||||
|
||||
query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!]) {
|
||||
comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort}) {
|
||||
...commentView
|
||||
action_summaries {
|
||||
count
|
||||
... on FlagActionSummary {
|
||||
reason
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user