mirror of
https://github.com/wassname/talk.git
synced 2026-07-04 17:57:43 +08:00
banUsers and moderate separate streams, not found assets etc
This commit is contained in:
@@ -4,8 +4,10 @@ import {
|
||||
FETCH_ASSETS_FAILURE,
|
||||
UPDATE_ASSET_STATE_REQUEST,
|
||||
UPDATE_ASSET_STATE_SUCCESS,
|
||||
UPDATE_ASSET_STATE_FAILURE
|
||||
UPDATE_ASSET_STATE_FAILURE,
|
||||
UPDATE_ASSETS
|
||||
} from '../constants/assets';
|
||||
|
||||
import coralApi from '../../../coral-framework/helpers/response';
|
||||
|
||||
/**
|
||||
@@ -34,3 +36,7 @@ export const updateAssetState = (id, closedAt) => (dispatch) => {
|
||||
dispatch({type: UPDATE_ASSET_STATE_SUCCESS}))
|
||||
.catch(error => dispatch({type: UPDATE_ASSET_STATE_FAILURE, error}));
|
||||
};
|
||||
|
||||
export const updateAssets = assets => dispatch => {
|
||||
dispatch({type: UPDATE_ASSETS, assets});
|
||||
};
|
||||
|
||||
@@ -101,12 +101,3 @@ export const flagComment = id => (dispatch, getState) => {
|
||||
dispatch({type: commentTypes.COMMENT_FLAG, id});
|
||||
dispatch({type: 'COMMENT_UPDATE', comment: getState().comments.get('byId').get(id)});
|
||||
};
|
||||
|
||||
// Dialog Actions
|
||||
export const showBanUserDialog = (userId, userName, commentId) => {
|
||||
return {type: commentTypes.SHOW_BANUSER_DIALOG, userId, userName, commentId};
|
||||
};
|
||||
|
||||
export const hideBanUserDialog = (showDialog) => {
|
||||
return {type: commentTypes.HIDE_BANUSER_DIALOG, showDialog};
|
||||
};
|
||||
|
||||
@@ -3,3 +3,12 @@ import * as actions from 'constants/moderation';
|
||||
export const setActiveTab = activeTab => ({type: actions.SET_ACTIVE_TAB, activeTab});
|
||||
export const toggleModal = open => ({type: actions.TOGGLE_MODAL, open});
|
||||
export const singleView = () => ({type: actions.SINGLE_VIEW});
|
||||
|
||||
// Ban User Dialog
|
||||
export const showBanUserDialog = (userId, userName, commentId) => {
|
||||
return {type: actions.SHOW_BANUSER_DIALOG, userId, userName, commentId};
|
||||
};
|
||||
|
||||
export const hideBanUserDialog = (showDialog) => {
|
||||
return {type: actions.HIDE_BANUSER_DIALOG, showDialog};
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
.rightPanel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 170px;
|
||||
height: 100%;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
export const FETCH_ASSETS_REQUEST = 'FETCH_ASSETS_REQUEST';
|
||||
export const FETCH_ASSETS_SUCCESS = 'FETCH_ASSETS_SUCCESS';
|
||||
export const FETCH_ASSETS_FAILURE = 'FETCH_ASSETS_FAILURE';
|
||||
|
||||
export const UPDATE_ASSET_STATE_REQUEST = 'UPDATE_ASSET_STATE_REQUEST';
|
||||
export const UPDATE_ASSET_STATE_SUCCESS = 'UPDATE_ASSET_STATE_SUCCESS';
|
||||
export const UPDATE_ASSET_STATE_FAILURE = 'UPDATE_ASSET_STATE_FAILURE';
|
||||
|
||||
export const UPDATE_ASSETS = 'UPDATE_ASSETS';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const SET_ACTIVE_TAB = 'SET_ACTIVE_TAB';
|
||||
export const TOGGLE_MODAL = 'TOGGLE_MODAL';
|
||||
export const SINGLE_VIEW = 'SINGLE_VIEW';
|
||||
export const SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG';
|
||||
export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG';
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
import React, {Component} from 'react';
|
||||
import key from 'keymaster';
|
||||
import {connect} from 'react-redux';
|
||||
import {compose} from 'react-apollo';
|
||||
import {Spinner} from 'coral-ui';
|
||||
import {withRouter} from 'react-router';
|
||||
import key from 'keymaster';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
import {modQueueQuery} from '../../graphql/queries';
|
||||
|
||||
import {
|
||||
updateStatus,
|
||||
showBanUserDialog,
|
||||
hideBanUserDialog,
|
||||
fetchPremodQueue,
|
||||
fetchRejectedQueue,
|
||||
fetchFlaggedQueue,
|
||||
fetchModerationQueueComments,
|
||||
} from 'actions/comments';
|
||||
|
||||
import {fetchSettings} from 'actions/settings';
|
||||
import {userStatusUpdate, sendNotificationEmail} from 'actions/users';
|
||||
import {updateAssets} from 'actions/assets';
|
||||
import {setActiveTab, toggleModal, singleView} from 'actions/moderation';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
import ModerationQueue from './ModerationQueue';
|
||||
import ModerationQueueHeader from './components/ModerationQueueHeader';
|
||||
import ModerationMenu from './components/ModerationMenu';
|
||||
import ModerationHeader from './components/ModerationHeader';
|
||||
import NotFoundAsset from './components/NotFoundAsset';
|
||||
|
||||
class ModerationContainer extends Component {
|
||||
|
||||
@@ -42,29 +34,37 @@ class ModerationContainer extends Component {
|
||||
key.unbind('esc');
|
||||
}
|
||||
|
||||
onTabClick = (activeTab) => {
|
||||
const {setActiveTab} = this.props;
|
||||
setActiveTab(activeTab);
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
const {toggleModal} = this.props;
|
||||
toggleModal(false);
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {updateAssets} = this.props;
|
||||
if(!isEqual(nextProps.data.assets, this.props.data.assets)) {
|
||||
updateAssets(nextProps.data.assets);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {data, moderation, settings} = this.props;
|
||||
const {data, moderation, settings, assets} = this.props;
|
||||
const providedAssetId = this.props.params.id;
|
||||
let asset;
|
||||
|
||||
if (data.loading) {
|
||||
return <div><Spinner/></div>;
|
||||
}
|
||||
|
||||
const enablePremodTab = data.premod.length;
|
||||
if (providedAssetId) {
|
||||
asset = assets.find(asset => asset.id === this.props.params.id);
|
||||
|
||||
if (!asset) {
|
||||
return <NotFoundAsset assetId={providedAssetId} />;
|
||||
}
|
||||
}
|
||||
|
||||
const enablePremodTab = !!data.premod.length;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ModerationQueueHeader
|
||||
onTabClick={this.onTabClick}
|
||||
<ModerationHeader asset={asset} />
|
||||
<ModerationMenu
|
||||
onTabClick={this.props.onTabClick}
|
||||
enablePremodTab={enablePremodTab}
|
||||
{...moderation} />
|
||||
<ModerationQueue
|
||||
@@ -80,33 +80,20 @@ class ModerationContainer extends Component {
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
moderation: state.moderation.toJS(),
|
||||
settings: state.settings.toJS()
|
||||
settings: state.settings.toJS(),
|
||||
assets: state.assets.get('assets')
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setActiveTab: tab => dispatch(setActiveTab(tab)),
|
||||
toggleModal: open => dispatch(toggleModal(open)),
|
||||
onTabClick: activeTab => dispatch(setActiveTab(activeTab)),
|
||||
toggleModal: toggle => dispatch(toggleModal(toggle)),
|
||||
onClose: () => dispatch(toggleModal(false)),
|
||||
singleView: () => dispatch(singleView()),
|
||||
|
||||
fetchSettings: () => dispatch(fetchSettings()),
|
||||
fetchModerationQueueComments: () => dispatch(fetchModerationQueueComments()),
|
||||
fetchPremodQueue: () => dispatch(fetchPremodQueue()),
|
||||
fetchRejectedQueue: () => dispatch(fetchRejectedQueue()),
|
||||
fetchFlaggedQueue: () => dispatch(fetchFlaggedQueue()),
|
||||
showBanUserDialog: (userId, userName, commentId) => dispatch(showBanUserDialog(userId, userName, commentId)),
|
||||
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
|
||||
userStatusUpdate: (status, userId, commentId) => dispatch(userStatusUpdate(status, userId, commentId)).then(() => {
|
||||
dispatch(fetchModerationQueueComments());
|
||||
}),
|
||||
suspendUser: (userId, subject, text) => dispatch(userStatusUpdate('suspended', userId))
|
||||
.then(() => dispatch(sendNotificationEmail(userId, subject, text)))
|
||||
.then(() => dispatch(fetchModerationQueueComments()))
|
||||
,
|
||||
updateStatus: (action, comment) => dispatch(updateStatus(action, comment))
|
||||
updateAssets: assets => dispatch(updateAssets(assets)),
|
||||
fetchSettings: () => dispatch(fetchSettings())
|
||||
});
|
||||
|
||||
export default compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withRouter,
|
||||
modQueueQuery
|
||||
)(ModerationContainer);
|
||||
|
||||
@@ -14,7 +14,6 @@ import translations from '../../../translations.json';
|
||||
|
||||
const Comment = props => {
|
||||
const links = linkify.getMatches(props.body);
|
||||
|
||||
return (
|
||||
<li tabIndex={props.index}
|
||||
className={`mdl-card mdl-shadow--2dp ${styles.listItem} ${props.isActive && !props.hideActive ? styles.activeItem : ''}`}>
|
||||
@@ -50,14 +49,24 @@ const Comment = props => {
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div className={styles.itemBody}> */}
|
||||
{/* Article title */}
|
||||
{/* <a>Moderate this Article</a> */}
|
||||
{/* </div> */}
|
||||
|
||||
<div className={styles.itemBody}>
|
||||
<span className={styles.body}>
|
||||
<p className={styles.body}>
|
||||
<Linkify component='span' properties={{style: linkStyles}}>
|
||||
<Highlighter searchWords={props.suspectWords} textToHighlight={props.body}/>
|
||||
</Linkify>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<span className={styles.context}><a>View context</a></span>
|
||||
|
||||
{/* <span className={styles.context}> */}
|
||||
{/* <a>View context</a> */}
|
||||
{/* </span> */}
|
||||
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router';
|
||||
import styles from './styles.css';
|
||||
|
||||
const ModerationHeader = props => (
|
||||
<div className=''>
|
||||
<div className={`mdl-tabs ${styles.header}`}>
|
||||
{
|
||||
props.asset ?
|
||||
<div className={`mdl-tabs__tab-bar ${styles.moderateAsset}`}>
|
||||
<Link className="mdl-tabs__tab" to="/admin/moderate">All Streams</Link>
|
||||
<a className="mdl-tabs__tab">{props.asset.title}</a>
|
||||
<Link className="mdl-tabs__tab" to="/admin/streams">Select Stream</Link>
|
||||
</div>
|
||||
:
|
||||
<div className={`mdl-tabs__tab-bar ${styles.moderateAsset}`}>
|
||||
<a className="mdl-tabs__tab" />
|
||||
<a className="mdl-tabs__tab">All Streams</a>
|
||||
<Link className="mdl-tabs__tab" to="/admin/streams">Select Stream</Link>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default ModerationHeader;
|
||||
+3
-11
@@ -5,7 +5,7 @@ import translations from '../../../translations.json';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const ModerationQueueHeader = (props) => (
|
||||
const ModerationMenu = (props) => (
|
||||
<div className='mdl-tabs'>
|
||||
<div className={`mdl-tabs__tab-bar ${styles.tabBar}`}>
|
||||
<a href='#all'
|
||||
@@ -29,14 +29,6 @@ const ModerationQueueHeader = (props) => (
|
||||
</a>
|
||||
: null
|
||||
}
|
||||
<a href='#account'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
props.onTabClick('account');
|
||||
}}
|
||||
className={`mdl-tabs__tab ${styles.tab} ${props.activeTab === 'account' ? styles.active : ''}`}>
|
||||
{lang.t('modqueue.account')}
|
||||
</a>
|
||||
<a href='#rejected'
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -59,9 +51,9 @@ const ModerationQueueHeader = (props) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
ModerationQueueHeader.propTypes = {
|
||||
ModerationMenu.propTypes = {
|
||||
activeTab: PropTypes.string.isRequired,
|
||||
enablePremodTab: PropTypes.bool
|
||||
};
|
||||
|
||||
export default ModerationQueueHeader;
|
||||
export default ModerationMenu;
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router';
|
||||
import styles from './styles.css';
|
||||
|
||||
const NotFound = props => (
|
||||
<div className={`mdl-card mdl-shadow--2dp ${styles.notFound}`}>
|
||||
<p>
|
||||
The provided asset id <Link to={`/admin/moderate/${props.assetId}`}>{props.assetId}</Link> does not exist.
|
||||
<Link className={styles.goToStreams} to="/admin/streams">Go to Streams</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default NotFound;
|
||||
@@ -1,3 +1,62 @@
|
||||
.notFound {
|
||||
position: relative;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
padding: 68px 45px;
|
||||
vertical-align: middle;
|
||||
min-width: 500px;
|
||||
|
||||
a {
|
||||
color: rgb(244, 126, 107);
|
||||
font-weight: 500;
|
||||
|
||||
&.goToStreams {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: #3949AB;
|
||||
color: white;
|
||||
margin-bottom: -1px;
|
||||
|
||||
.moderateAsset {
|
||||
a {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
color: white;
|
||||
text-transform: capitalize;
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
letter-spacing: 1px;
|
||||
transition: opacity 200ms;
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
opacity: .8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@custom-media --big-viewport (min-width: 780px);
|
||||
|
||||
.list {
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
|
||||
a {
|
||||
color: rgb(44, 44, 44);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,10 @@ import {connect} from 'react-redux';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import {fetchAssets, updateAssetState} from '../../actions/assets';
|
||||
import translations from '../../translations.json';
|
||||
import {
|
||||
RadioGroup,
|
||||
Radio,
|
||||
Icon,
|
||||
DataTable,
|
||||
TableHeader
|
||||
} from 'react-mdl';
|
||||
import Pager from 'coral-ui/components/Pager';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
import {Pager, Icon} from 'coral-ui';
|
||||
import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl';
|
||||
|
||||
const limit = 25;
|
||||
|
||||
@@ -74,6 +70,8 @@ class Streams extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderTitle = (title, {id}) => <Link to={`/admin/moderate/${id}`}>{title}</Link>
|
||||
|
||||
renderStatus = (closedAt, {id}) => {
|
||||
const closed = closedAt && new Date(closedAt).getTime() < Date.now();
|
||||
const statusMenuOpen = this.state.statusMenus[id];
|
||||
@@ -104,6 +102,9 @@ class Streams extends Component {
|
||||
render () {
|
||||
const {search, sort, filter} = this.state;
|
||||
const {assets} = this.props;
|
||||
|
||||
const assetsIds = assets.ids.map((id) => assets.byId[id]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.leftColumn}>
|
||||
@@ -142,16 +143,14 @@ class Streams extends Component {
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className={styles.mainContent}>
|
||||
<DataTable
|
||||
className={styles.streamsTable}
|
||||
rows={assets.ids.map((id) => assets.byId[id])}>
|
||||
<TableHeader name="title">{lang.t('streams.article')}</TableHeader>
|
||||
<TableHeader name="publication_date" cellFormatter={this.renderDate}>
|
||||
{lang.t('streams.pubdate')}
|
||||
</TableHeader>
|
||||
<TableHeader name="closedAt" cellFormatter={this.renderStatus} className={styles.status}>
|
||||
{lang.t('streams.status')}
|
||||
</TableHeader>
|
||||
<DataTable className={styles.streamsTable} rows={assetsIds} onClick={this.goToModeration}>
|
||||
<TableHeader name="title" cellFormatter={this.renderTitle}>{lang.t('streams.article')}</TableHeader>
|
||||
<TableHeader name="publication_date" cellFormatter={this.renderDate}>
|
||||
{lang.t('streams.pubdate')}
|
||||
</TableHeader>
|
||||
<TableHeader name="closedAt" cellFormatter={this.renderStatus} className={styles.status}>
|
||||
{lang.t('streams.status')}
|
||||
</TableHeader>
|
||||
</DataTable>
|
||||
<Pager
|
||||
totalPages={Math.ceil((assets.count || 0) / limit)}
|
||||
@@ -169,6 +168,7 @@ const mapStateToProps = ({assets}) => {
|
||||
assets: assets.toJS()
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
fetchAssets: (...args) => {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import translations from '../../translations.json';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const StreamsTable = props => (
|
||||
<table className={'mdl-data-table'}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="mdl-data-table__cell--non-numeric">
|
||||
{lang.t('streams.article')}
|
||||
</th>
|
||||
<th className="mdl-data-table__cell--non-numeric">
|
||||
{lang.t('streams.pubdate')}
|
||||
</th>
|
||||
<th className="mdl-data-table__cell--non-numeric">
|
||||
{lang.t('streams.status')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.rows.map((row, i)=> (
|
||||
<tr key={i}>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.title}
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{row.publication_date}
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
{lang.t('streams.status')}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
export default StreamsTable;
|
||||
@@ -0,0 +1,6 @@
|
||||
query Assets {
|
||||
assets {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#import "../fragments/commentView.graphql"
|
||||
|
||||
query ($asset_id: ID!) {
|
||||
query ModQueue ($asset_id: ID!) {
|
||||
all: comments(query: {
|
||||
statuses: [ACCEPTED, REJECTED, PREMOD],
|
||||
asset_id: $asset_id
|
||||
@@ -25,10 +25,8 @@ query ($asset_id: ID!) {
|
||||
}) {
|
||||
...commentView
|
||||
}
|
||||
account: comments(query: {
|
||||
statuses: [ACCEPTED],
|
||||
asset_id: $asset_id
|
||||
}) {
|
||||
...commentView
|
||||
assets: assets {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import {Map, List, fromJS} from 'immutable';
|
||||
import {FETCH_ASSETS_SUCCESS, UPDATE_ASSET_STATE_REQUEST} from '../constants/assets';
|
||||
import * as actions from '../constants/assets';
|
||||
|
||||
const initialState = Map({
|
||||
byId: Map(),
|
||||
ids: List()
|
||||
ids: List(),
|
||||
assets: List()
|
||||
});
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
export default function assets (state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case FETCH_ASSETS_SUCCESS:
|
||||
case actions.FETCH_ASSETS_SUCCESS:
|
||||
return replaceAssets(action, state);
|
||||
case UPDATE_ASSET_STATE_REQUEST:
|
||||
return state.setIn(['byId', action.id, 'closedAt'], action.closedAt);
|
||||
default: return state;
|
||||
case actions.UPDATE_ASSET_STATE_REQUEST:
|
||||
return state
|
||||
.setIn(['byId', action.id, 'closedAt'], action.closedAt);
|
||||
case actions.UPDATE_ASSETS:
|
||||
return state
|
||||
.set('assets', List(action.assets));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const replaceAssets = (action, state) => {
|
||||
const assets = fromJS(action.assets.reduce((prev, curr) => { prev[curr.id] = curr; return prev; }, {}));
|
||||
|
||||
@@ -2,6 +2,7 @@ const _ = require('lodash');
|
||||
|
||||
const Comment = require('./comment');
|
||||
const Action = require('./action');
|
||||
const User = require('./user');
|
||||
|
||||
module.exports = (context) => {
|
||||
|
||||
@@ -9,6 +10,7 @@ module.exports = (context) => {
|
||||
return _.merge(...[
|
||||
Comment,
|
||||
Action,
|
||||
User,
|
||||
].map((mutators) => {
|
||||
|
||||
// Each set of mutators is a function which takes the context.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const UsersService = require('../../services/users');
|
||||
|
||||
const setUserStatus = ({user}, {id, status}) => {
|
||||
console.log('------as-d-asd-a-sads-a-sad-dsa-----');
|
||||
console.log('user', user);
|
||||
console.log('id', id);
|
||||
console.log('status', status);
|
||||
|
||||
return UsersService.setStatus(id, status)
|
||||
.then((user) => {
|
||||
console.log('result', user);
|
||||
return user;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = (context) => {
|
||||
|
||||
// TODO: refactor to something that'll return an error in the event an attempt
|
||||
// is made to mutate state while not logged in. There's got to be a better way
|
||||
// to do this.
|
||||
if (context.user && context.user.can('mutation:setUserStatus')) {
|
||||
return {
|
||||
User: {
|
||||
setUserStatus: (action) => setUserStatus(context, action)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
User: {
|
||||
setUserStatus: () => {},
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -8,6 +8,9 @@ const RootMutation = {
|
||||
deleteAction(_, {id}, {mutators: {Action}}) {
|
||||
return Action.delete({id});
|
||||
},
|
||||
setUserStatus(_, {id, status}, {mutators: {User}}) {
|
||||
return User.setUserStatus({id, status});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = RootMutation;
|
||||
|
||||
@@ -61,6 +61,9 @@ type User {
|
||||
|
||||
# returns all comments based on a query.
|
||||
comments(query: CommentsQuery): [Comment]
|
||||
|
||||
# returns user status
|
||||
status: USER_STATUS
|
||||
}
|
||||
|
||||
type Comment {
|
||||
@@ -177,6 +180,13 @@ enum COMMENT_STATUS {
|
||||
PREMOD
|
||||
}
|
||||
|
||||
enum USER_STATUS {
|
||||
ACTIVE
|
||||
BANNED
|
||||
PENDING
|
||||
APPROVED
|
||||
}
|
||||
|
||||
type RootQuery {
|
||||
|
||||
# retrieves site wide settings and defaults.
|
||||
@@ -216,6 +226,8 @@ type RootMutation {
|
||||
# delete an action based on the action id.
|
||||
deleteAction(id: ID!): Boolean
|
||||
|
||||
# sets user status
|
||||
setUserStatus(id: ID!, status: USER_STATUS!): Boolean
|
||||
}
|
||||
|
||||
schema {
|
||||
|
||||
+2
-1
@@ -147,7 +147,8 @@ const USER_GRAPH_OPERATIONS = [
|
||||
'mutation:createComment',
|
||||
'mutation:createAction',
|
||||
'mutation:deleteAction',
|
||||
'mutation:editName'
|
||||
'mutation:editName',
|
||||
'mutation:setUserStatus'
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user