diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index cd1a3e59f..9968f25d3 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -29,7 +29,6 @@ class ModerationContainer extends Component {
componentWillMount() {
const {toggleModal, singleView} = this.props;
- this.props.fetchModerationQueueComments();
this.props.fetchSettings();
key('s', () => singleView());
@@ -54,7 +53,7 @@ class ModerationContainer extends Component {
}
render () {
- const {data, moderation} = this.props;
+ const {data, moderation, settings} = this.props;
if (data.loading) {
return
;
@@ -66,27 +65,16 @@ class ModerationContainer extends Component {
);
}
}
-// ModerationContainer.contextTypes = {
-// router: React.PropTypes.func.isRequired
-// };
-
const mapStateToProps = state => ({
moderation: state.moderation.toJS(),
- comments: state.comments.toJS(),
- settings: state.settings.toJS(),
- users: state.users.toJS(),
- actions: state.actions.toJS()
+ settings: state.settings.toJS()
});
const mapDispatchToProps = dispatch => ({
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
index 961642e84..f1f3514ea 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
@@ -1,22 +1,29 @@
-import React from 'react';
+import React, {PropTypes} from 'react';
-// import Comment from '../../components/Comment';
+import Comment from './components/Comment';
-export default (props) => {
+const ModerationQueue = props => {
return (
- {/*
*/}
+
{
- props.data[props.activeTab].map((comment, i) =>
-
- {comment.body}
-
- )
+ props.data[props.activeTab].map((comment, i) => {
+ console.log(comment);
+ return ;
+ })
}
+
);
};
+
+ModerationQueue.propTypes = {
+ data: PropTypes.object.isRequired
+};
+
+export default ModerationQueue;
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
new file mode 100644
index 000000000..a06f052e4
--- /dev/null
+++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
@@ -0,0 +1,76 @@
+import React, {PropTypes} from 'react';
+import timeago from 'timeago.js';
+import Linkify from 'react-linkify';
+import Highlighter from 'react-highlight-words';
+
+import styles from './styles.css';
+
+import {Icon} from 'coral-ui';
+
+// import ActionButton from '../components/ActionButton';
+
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from '../../../translations.json';
+
+const Comment = props => {
+ const links = linkify.getMatches(props.body);
+
+ return (
+
+
+
+
{props.user.name}
+
+ {timeago().format(props.created_at || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}
+
+ {props.flagged ?
{lang.t('comment.flagged')}
: null}
+
+
+ {links ?
Contains Link : null}
+
+ {/* {props.modActions.map(*/}
+ {/* (action, i) =>*/}
+ {/*
*/}
+ {/* )}*/}
+
+ {props.user.banned === 'banned' ?
+
+
+ {lang.t('comment.banned_user')}
+
+ : null}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const linkStyles = {
+ backgroundColor: 'rgb(255, 219, 135)',
+ padding: '1px 2px'
+};
+
+const linkify = new Linkify();
+const lang = new I18n(translations);
+
+Comment.propTypes = {
+ user: PropTypes.object.isRequired
+};
+
+export default Comment;
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationList.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationList.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/styles.css b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
new file mode 100644
index 000000000..fbcbce7d7
--- /dev/null
+++ b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
@@ -0,0 +1,184 @@
+
+@custom-media --big-viewport (min-width: 780px);
+
+.list {
+ padding: 8px 0;
+ list-style: none;
+ display: block;
+
+ &.singleView .listItem {
+ display: none;
+ }
+
+ &.singleView .listItem.activeItem {
+ display: block;
+ height: 100%;
+ font-size: 1.5em;
+ line-height: 1.5em;
+ border: none;
+
+ .actions {
+ position: fixed;
+ bottom: 60px;
+ left: 25%;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-around;
+ width: 50%;
+ margin: 0;
+ }
+
+ .actionButton {
+ transform: scale(1.4);
+ }
+ }
+}
+
+.listItem {
+ border-bottom: 1px solid #e0e0e0;
+ font-size: 16px;
+ width: 100%;
+ max-width: 660px;
+ min-width: 400px;
+ margin: 0 auto;
+ padding: 16px 14px;
+ position: relative;
+ transition: box-shadow 200ms;
+
+
+ &:hover {
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+ }
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ .sideActions {
+ position: absolute;
+ right: 0;
+ height: 100%;
+ top: 0;
+ padding: 40px 18px;
+ box-sizing: border-box;
+ }
+
+ .itemHeader {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .author {
+ min-width: 230px;
+ display: flex;
+ align-items: center;
+ }
+ }
+
+ .itemBody {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .avatar {
+ margin-right: 16px;
+ height: 40px;
+ width: 40px;
+ border-radius: 50%;
+ background-color: #757575;
+ font-size: 40px;
+ color: #fff;
+ }
+
+ .created {
+ color: #666;
+ font-size: 13px;
+ margin-left: 40px;
+ }
+
+ .actionButton {
+ transform: scale(.8);
+ margin: 0;
+ }
+
+ .body {
+ margin-top: 20px;
+ flex: 1;
+ font-size: 0.88em;
+ color: black;
+ }
+
+ .flagged {
+ color: rgba(255, 0, 0, .5);
+ padding-top: 15px;
+ padding-left: 10px;
+ }
+
+ .flagCount{
+ font-size: 12px;
+ color: #d32f2f;
+ }
+
+}
+
+.empty {
+ color: #444;
+ margin-top: 50px;
+ text-align: center;
+}
+
+
+@media (--big-viewport) {
+ .listItem {
+ border: 1px solid #e0e0e0;
+ margin-bottom: 30px;
+
+ &:last-child {
+ border-bottom: 1px solid #e0e0e0;
+ }
+
+ &.activeItem {
+ border: 2px solid #333;
+ }
+ }
+
+}
+
+.hasLinks {
+ color: #f00;
+ text-align: right;
+ display: flex;
+ align-items: center;
+
+ i {
+ margin-right: 5px;
+ }
+}
+
+.banned {
+ color: #f00;
+ text-align: left;
+ display: flex;
+ align-items: center;
+
+ i {
+ margin-right: 5px;
+ }
+}
+
+.ban {
+ display: block;
+ text-align: center;
+ margin-top: 5px;
+}
+
+.banButton {
+ width: 114px;
+ letter-spacing: 1px;
+
+ i {
+ vertical-align: middle;
+ margin-right: 10px;
+ font-size: 14px;
+ }
+}
diff --git a/client/coral-admin/src/reducers/actions.js b/client/coral-admin/src/reducers/actions.js
deleted file mode 100644
index 284e41c72..000000000
--- a/client/coral-admin/src/reducers/actions.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import {Map, Set, fromJS} from 'immutable';
-import * as types from '../constants/actions';
-
-const initialState = Map({
- ids: Set(),
- byId: Map()
-});
-
-export default (state = initialState, action) => {
- switch (action.type) {
- case types.ACTIONS_MODERATION_QUEUE_FETCH_SUCCESS: return addActions(state, action);
- default:
- return state;
- }
-};
-
-const addActions = (state, action) => {
-
- // Make ids that are unique by item_id and by action type
- const typeId = (action) => `${action.action_type}_${action.item_id}`;
- const ids = action.actions.map(action => typeId(action));
- const map = action.actions.reduce((memo, action) => {
- memo[typeId(action)] = action;
- return memo;
- }, {});
- return state.set('byId', fromJS(map)).set('ids', new Set(ids));
-};
diff --git a/client/coral-admin/src/reducers/comments.js b/client/coral-admin/src/reducers/comments.js
deleted file mode 100644
index 034016088..000000000
--- a/client/coral-admin/src/reducers/comments.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import * as actions from '../constants/comments';
-import * as userActions from '../constants/users';
-import {Map, List, fromJS} from 'immutable';
-
-/**
- * Comments state is stored using 2 structures:
- * - byId is a Map holding the comments using the item_id property as keys
- * - ids is a List of item_id, this allows us to order and iterate easily
- * since maps are unordered and some times we just need a list of things
- */
-
-const initialState = Map({
- byId: Map(),
- ids: List(),
- loading: false,
- showBanUserDialog: false,
- banUser: {
- 'userName': '',
- 'userId': '',
- 'commentId': ''
- }
-});
-
-// Handle the comment actions
-export default (state = initialState, action) => {
- switch (action.type) {
- case actions.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST: return state.set('loading', true);
- case actions.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS: return replaceComments(action, state);
- case actions.COMMENTS_MODERATION_QUEUE_FAILED: return state.set('loading', false);
- case actions.COMMENT_STATUS_UPDATE_REQUEST: return updateStatus(state, action);
- case actions.COMMENT_FLAG: return flag(state, action);
- case actions.COMMENT_CREATE_SUCCESS: return addComment(state, action);
- case actions.COMMENT_STREAM_FETCH_SUCCESS: return replaceComments(action, state);
- case actions.SHOW_BANUSER_DIALOG: return setBanUser(state, true, action);
- case actions.HIDE_BANUSER_DIALOG: return setBanUser(state, false, action);
- case actions.USER_BAN_SUCCESS: return setBanUser(state, false, action);
- case userActions.UPDATE_STATUS_SUCCESS: return setBanUser(state, false, action);
- default: return state;
- }
-};
-
-// hide or show the UI for the dialog confirming the ban
-// set the user that is going to set and the comment that is the reason
-const setBanUser = (state, showBanUser, action) => {
- const banUser = {'userName': action.userName, 'userId': action.userId, 'commentId': action.commentId};
- return state.set('showBanUserDialog', showBanUser)
- .set('banUser', banUser);
-};
-
-// Update a comment status
-const updateStatus = (state, action) => {
- const byId = state.get('byId');
- const data = byId.get(action.id).set('status', action.status.toLowerCase());
- return state.set('byId', byId.set(action.id, data));
-};
-
-// Flag a comment
-const flag = (state, action) => {
- const byId = state.get('byId');
- const data = byId.get(action.id).set('flagged', true);
- const comment = byId.get(action.id).set('data', data);
- return state.set('byId', byId.set(action.id, comment));
-};
-
-// Replace the comment list with a new one
-const replaceComments = (action, state) => {
- const comments = fromJS(action.comments.reduce((prev, curr) => { prev[curr.id] = curr; return prev; }, {}));
- return state.set('byId', comments).set('loading', false)
- .set('ids', List(comments.keys()));
-};
-
-// Add a new comment
-const addComment = (state, action) => {
- const comment = fromJS(action.comment);
- return state.set('byId', state.get('byId').set(comment.get('item_id'), comment))
- .set('ids', state.get('ids').unshift(comment.get('item_id')));
-};
diff --git a/client/coral-admin/src/reducers/index.js b/client/coral-admin/src/reducers/index.js
index 53256fac5..e58f5bf5a 100644
--- a/client/coral-admin/src/reducers/index.js
+++ b/client/coral-admin/src/reducers/index.js
@@ -1,19 +1,13 @@
import auth from './auth';
-import users from './users';
import assets from './assets';
-import actions from './actions';
-import comments from './comments';
import settings from './settings';
import community from './community';
import moderation from './moderation';
export default {
auth,
- users,
assets,
- actions,
settings,
- comments,
community,
moderation
};
diff --git a/client/coral-admin/src/reducers/settings.js b/client/coral-admin/src/reducers/settings.js
index 12b16d9ad..fa728b3ed 100644
--- a/client/coral-admin/src/reducers/settings.js
+++ b/client/coral-admin/src/reducers/settings.js
@@ -1,5 +1,5 @@
import {Map, List} from 'immutable';
-import * as types from '../actions/settings';
+import * as actions from '../actions/settings';
const initialState = Map({
settings: Map({
@@ -13,43 +13,46 @@ const initialState = Map({
fetchingSettings: false
});
-// Handle the comment actions
-export default (state = initialState, action) => {
+export default function settings (state = initialState, action) {
switch (action.type) {
- case types.SETTINGS_LOADING: return state.set('fetchingSettings', true).set('fetchSettingsError', null);
- case types.SETTINGS_RECEIVED: return updateSettings(state, action);
- case types.SETTINGS_FETCH_ERROR: return settingsFetchFailed(state, action);
- case types.SETTINGS_UPDATED: return updateSettings(state, action);
- case types.SAVE_SETTINGS_LOADING: return state.set('fetchingSettings', true).set('saveSettingsError', null);
- case types.SAVE_SETTINGS_SUCCESS: return saveComplete(state, action);
- case types.SAVE_SETTINGS_FAILED: return settingsSaveFailed(state, action);
- case types.WORDLIST_UPDATED: return updateWordlist(state, action);
- default: return state;
+ case actions.SETTINGS_LOADING:
+ return state
+ .set('fetchingSettings', true)
+ .set('fetchSettingsError', null);
+ case actions.SETTINGS_RECEIVED:
+ return state.merge({
+ fetchingSettings: null,
+ fetchSettingsError: null,
+ ...action.settings
+ });
+ case actions.SETTINGS_FETCH_ERROR:
+ return state
+ .set('fetchingSettings', false)
+ .set('fetchSettingsError', action.error);
+ case actions.SETTINGS_UPDATED:
+ return state.merge({
+ fetchingSettings: null,
+ fetchSettingsError: null,
+ ...action.settings
+ });
+ case actions.SAVE_SETTINGS_LOADING:
+ return state
+ .set('fetchingSettings', true)
+ .set('saveSettingsError', null);
+ case actions.SAVE_SETTINGS_SUCCESS:
+ return state.merge({
+ fetchingSettings: false,
+ fetchSettingsError: null,
+ ...action.settings
+ });
+ case actions.SAVE_SETTINGS_FAILED:
+ return state
+ .set('fetchingSettings', false)
+ .set('fetchSettingsError', action.error);
+ case actions.WORDLIST_UPDATED:
+ return state
+ .setIn(['settings', 'wordlist', action.listName], action.list);
+ default:
+ return state;
}
-};
-
-// only for updating top-level settings
-const updateSettings = (state, action) => {
- const s = state.set('fetchingSettings', false).set('fetchSettingsError', null);
- const settings = s.get('settings').merge(action.settings);
- return s.set('settings', settings);
-};
-
-// any nested settings must have a specialized setter
-const updateWordlist = (state, action) => {
- return state.setIn(['settings', 'wordlist', action.listName], action.list);
-};
-
-const saveComplete = (state, action) => {
- const s = state.set('fetchingSettings', false).set('saveSettingsError', null);
- const settings = s.get('settings').merge(action.settings);
- return s.set('settings', settings);
-};
-
-const settingsFetchFailed = (state, action) => {
- return state.set('fetchingSettings', false).set('fetchSettingsError', action.error);
-};
-
-const settingsSaveFailed = (state, action) => {
- return state.set('fetchingSettings', false).set('fetchSettingsError', action.error);
-};
+}
diff --git a/client/coral-admin/src/reducers/users.js b/client/coral-admin/src/reducers/users.js
deleted file mode 100644
index ef589c155..000000000
--- a/client/coral-admin/src/reducers/users.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import {Map, List, fromJS} from 'immutable';
-
-const initialState = Map({
- byId: Map(),
- ids: List()
-});
-
-export default (state = initialState, action) => {
- switch (action.type) {
- case 'USERS_MODERATION_QUEUE_FETCH_SUCCESS': return replaceUsers(action, state);
- case 'USER_STATUS_UPDATE': return updateUserStatus(state, action);
- default: return state;
- }
-};
-
-// Replace the comment list with a new one
-const replaceUsers = (action, state) => {
- const users = fromJS(action.users.reduce((prev, curr) => { prev[curr.id] = curr; return prev; }, {}));
- return state.set('byId', users)
- .set('ids', List(users.keys()));
-};
-
-// Update a user status
-const updateUserStatus = (state, action) => {
- const byId = state.get('byId');
- const data = byId.get(action.author_id).set('status', action.status.toLowerCase());
- return state.set('byId', byId.set(action.author_id, data));
-};
diff --git a/client/coral-admin/src/services/client.js b/client/coral-admin/src/services/client.js
index b4a7a38df..40a539634 100644
--- a/client/coral-admin/src/services/client.js
+++ b/client/coral-admin/src/services/client.js
@@ -2,7 +2,6 @@ import ApolloClient, {addTypename} from 'apollo-client';
import getNetworkInterface from './transport';
export const client = new ApolloClient({
- connectToDevTools: true,
queryTransformer: addTypename,
dataIdFromObject: (result) => {
if (result.id && result.__typename) { // eslint-disable-line no-underscore-dangle