diff --git a/.eslintignore b/.eslintignore index fc0d50a25..cd0c80673 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,13 @@ dist +docs client/lib **/*.html plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect +!plugins/coral-plugin-offtopic +!plugins/coral-plugin-like +!plugins/coral-plugin-mod +!plugins/coral-plugin-love node_modules diff --git a/.gitignore b/.gitignore index f0c3b2a4b..11e738d57 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ coverage/ plugins.json plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic !plugins/coral-plugin-like diff --git a/PLUGINS.md b/PLUGINS.md index 4462f8577..8bac10aba 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -280,6 +280,41 @@ send data to the client. If the type in question contains args, clients may subs For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions). +#### Field: `tokenUserNotFound` + +```js +tokenUserNotFound: async ({jwt, token}) => { + let profile = await someExternalService(token); + if (!profile) { + return null; + } + + let user = await UserModel.findOneAndUpdate({ + id: profile.id + }, { + id: profile.id, + username: profile.username, + lowercaseUsername: profile.username.toLowerCase(), + roles: [], + profiles: [] + }, { + setDefaultsOnInsert: true, + new: true, + upsert: true + }); + + return user; +} +``` + +The `tokenUserNotFound` hook allows auth integrations to hook into the event +when a valid token is provided but a user can't be found in the database that +matches the provided id. + +The function is async, and should return the user object that was created in the +database, or null if the user wasn't found. The `jwt` paramenter of the object +is the unpacked token, while `token` is the original jwt token string. + #### Field: `router` ```js diff --git a/README.md b/README.md index 718847d9e..48055e326 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ To set up a development environment or build from source, see [INSTALL.md](https To launch a Talk server of your own from your browser without any need to muck about in a terminal or think about engineering concepts, stay tuned. We will launch [our installer](https://github.com/coralproject/talk-install) shortly! - ### Configuration The Talk application looks for the following configuration values either as environment variables: @@ -45,6 +44,12 @@ sign and verify tokens via a `HS256` algorithm. Refer to the wiki page on [Configuration Loading](https://github.com/coralproject/talk/wiki/Configuration-Loading) for alternative methods of loading configuration during development. +### Using Trust + +Talk ships with core components we call "Trust". This allows Talk to automate certain actions based on previous user behavior. + +Our first feature is the notion of Karma. Talk will automatically pre-moderate comments of users who have a negative karma score. You can [see more how karma works here](/services/karma.js). + ## Supported Browsers ### Web @@ -58,6 +63,8 @@ alternative methods of loading configuration during development. - iPad - iPad Pro +- iPhone 7 Plus +- iPhone 7 - iPhone 6 Plus - iPhone 6 - iPhone 5 diff --git a/app.js b/app.js index 16840f965..959aed0a3 100644 --- a/app.js +++ b/app.js @@ -6,11 +6,14 @@ const helmet = require('helmet'); const authentication = require('./middleware/authentication'); const {passport} = require('./services/passport'); const plugins = require('./services/plugins'); +const i18n = require('./services/i18n'); const enabled = require('debug').enabled; const errors = require('./errors'); const {createGraphOptions} = require('./graph'); const apollo = require('graphql-server-express'); const accepts = require('accepts'); +const compression = require('compression'); +const cookieParser = require('cookie-parser'); const app = express(); @@ -31,6 +34,8 @@ app.set('trust proxy', 1); app.use(helmet({ frameguard: false })); +app.use(compression()); +app.use(cookieParser()); app.use(bodyParser.json()); //============================================================================== @@ -153,6 +158,8 @@ app.use('/', (err, req, res, next) => { console.error(err); } + i18n.init(req); + if (err instanceof errors.APIError) { res.status(err.status); res.render('error', { diff --git a/bin/cli-assets b/bin/cli-assets index 166152baf..97b5b043c 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -5,7 +5,7 @@ */ const program = require('./commander'); -const parseDuration = require('parse-duration'); +const parseDuration = require('ms'); const Table = require('cli-table'); const AssetModel = require('../models/asset'); const mongoose = require('../services/mongoose'); diff --git a/client/coral-admin/config.json b/client/coral-admin/config.json deleted file mode 100644 index 156740de5..000000000 --- a/client/coral-admin/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "basePath": "admin" -} diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index 989316dd9..3bde5f931 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -1,25 +1,20 @@ import React from 'react'; import {Router, Route, IndexRedirect, browserHistory} from 'react-router'; -import Stories from 'containers/Stories/Stories'; -import Configure from 'containers/Configure/Configure'; -import LayoutContainer from 'containers/LayoutContainer'; -import InstallContainer from 'containers/Install/InstallContainer'; +import Configure from 'routes/Configure'; +import Dashboard from 'routes/Dashboard'; +import Install from 'routes/Install'; +import Stories from 'routes/Stories'; +import {CommunityLayout, Community} from 'routes/Community'; +import {ModerationLayout, Moderation} from 'routes/Moderation'; -import CommunityLayout from 'containers/Community/CommunityLayout'; -import CommunityContainer from 'containers/Community/CommunityContainer'; - -import ModerationLayout from 'containers/ModerationQueue/ModerationLayout'; -import ModerationContainer from 'containers/ModerationQueue/ModerationContainer'; - -import Dashboard from 'containers/Dashboard/Dashboard'; +import Layout from 'containers/Layout'; const routes = (
- - + + - @@ -27,11 +22,11 @@ const routes = ( {/* Community Routes */} - - + + - - + + @@ -39,22 +34,22 @@ const routes = ( {/* Moderation Routes */} - - + + - - + + - - + + - - + + - - + + - + diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/assets.js index f5c2f848a..8a18a8e3f 100644 --- a/client/coral-admin/src/actions/assets.js +++ b/client/coral-admin/src/actions/assets.js @@ -8,7 +8,7 @@ import { UPDATE_ASSETS } from '../constants/assets'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; /** * Action disptacher related to assets diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 29971e294..ca48764f1 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,6 +1,7 @@ +import bowser from 'bowser'; import * as actions from '../constants/auth'; +import coralApi from 'coral-framework/helpers/request'; import * as Storage from 'coral-framework/helpers/storage'; -import coralApi from 'coral-framework/helpers/response'; import {handleAuthToken} from 'coral-framework/actions/auth'; //============================================================================== @@ -9,16 +10,31 @@ import {handleAuthToken} from 'coral-framework/actions/auth'; export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => { dispatch({type: actions.LOGIN_REQUEST}); - const params = {method: 'POST', body: {email, password}}; + + const params = { + method: 'POST', + body: { + email, + password + } + }; + if (recaptchaResponse) { - params.headers = {'X-Recaptcha-Response': recaptchaResponse}; + params.headers = { + 'X-Recaptcha-Response': recaptchaResponse + }; } + return coralApi('/auth/local', params) .then(({user, token}) => { + if (!user) { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } + dispatch(handleAuthToken(token)); dispatch(checkLoginSuccess(user)); }) @@ -52,7 +68,9 @@ const forgotPassowordFailure = () => ({ export const requestPasswordReset = (email) => (dispatch) => { dispatch(forgotPassowordRequest(email)); - return coralApi('/account/password/reset', {method: 'POST', body: {email}}) + const redirectUri = location.href; + + return coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}}) .then(() => dispatch(forgotPassowordSuccess())) .catch((error) => dispatch(forgotPassowordFailure(error))); }; @@ -81,7 +99,9 @@ export const checkLogin = () => (dispatch) => { return coralApi('/auth') .then(({user}) => { if (!user) { - Storage.removeItem('token'); + if (!bowser.safari && !bowser.ios) { + Storage.removeItem('token'); + } return dispatch(checkLoginFailure('not logged in')); } diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js index 9a8743762..36b9ffb0a 100644 --- a/client/coral-admin/src/actions/community.js +++ b/client/coral-admin/src/actions/community.js @@ -14,7 +14,7 @@ import { HIDE_SUSPENDUSER_DIALOG } from '../constants/community'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const fetchAccounts = (query = {}) => (dispatch) => { diff --git a/client/coral-admin/src/actions/install.js b/client/coral-admin/src/actions/install.js index 21ff58de1..15413e220 100644 --- a/client/coral-admin/src/actions/install.js +++ b/client/coral-admin/src/actions/install.js @@ -1,4 +1,4 @@ -import coralApi from 'coral-framework/helpers/response'; +import coralApi from 'coral-framework/helpers/request'; import * as actions from '../constants/install'; import validate from 'coral-framework/helpers/validate'; import errorMsj from 'coral-framework/helpers/error'; diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js index c5e6fc620..37e09590d 100644 --- a/client/coral-admin/src/actions/moderation.js +++ b/client/coral-admin/src/actions/moderation.js @@ -5,7 +5,7 @@ export const singleView = () => ({type: actions.SINGLE_VIEW}); // Ban User Dialog export const showBanUserDialog = (user, commentId, commentStatus, showRejectedNote) => ({type: actions.SHOW_BANUSER_DIALOG, user, commentId, commentStatus, showRejectedNote}); -export const hideBanUserDialog = (showDialog) => ({type: actions.HIDE_BANUSER_DIALOG, showDialog}); +export const hideBanUserDialog = () => ({type: actions.HIDE_BANUSER_DIALOG}); // Suspend User Dialog export const showSuspendUserDialog = (userId, username, commentId, commentStatus) => @@ -27,3 +27,27 @@ export const hideShortcutsNote = () => { export const viewUserDetail = (userId) => ({type: actions.VIEW_USER_DETAIL, userId}); export const hideUserDetail = () => ({type: actions.HIDE_USER_DETAIL}); + +export const setSortOrder = (order) => ({ + type: actions.SET_SORT_ORDER, + order +}); + +export const changeUserDetailStatuses = (tab) => { + let statuses; + if (tab === 'all') { + statuses = ['NONE', 'ACCEPTED', 'REJECTED', 'PREMOD']; + } else if (tab === 'rejected') { + statuses = ['REJECTED']; + } + return {type: actions.CHANGE_USER_DETAIL_STATUSES, tab, statuses}; +}; + +export const clearUserDetailSelections = () => ({type: actions.CLEAR_USER_DETAIL_SELECTIONS}); + +export const toggleSelectCommentInUserDetail = (id, active) => { + return { + type: active ? actions.SELECT_USER_DETAIL_COMMENT : actions.UNSELECT_USER_DETAIL_COMMENT, + id + }; +}; diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index b0530e77e..65c200420 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; export const SETTINGS_LOADING = 'SETTINGS_LOADING'; export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED'; diff --git a/client/coral-admin/src/actions/users.js b/client/coral-admin/src/actions/users.js index 2642db90d..e888d5b74 100644 --- a/client/coral-admin/src/actions/users.js +++ b/client/coral-admin/src/actions/users.js @@ -1,4 +1,4 @@ -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from '../../../coral-framework/helpers/request'; import * as userTypes from '../constants/users'; /** diff --git a/client/coral-admin/src/components/ActionButton.js b/client/coral-admin/src/components/ActionButton.js index 3651ee939..e084d8e7a 100644 --- a/client/coral-admin/src/components/ActionButton.js +++ b/client/coral-admin/src/components/ActionButton.js @@ -1,25 +1,27 @@ import React, {PropTypes} from 'react'; import styles from './ModerationList.css'; import {Button} from 'coral-ui'; -import {menuActionsMap} from '../containers/ModerationQueue/helpers/moderationQueueActionsMap'; +import {menuActionsMap} from '../routes/Moderation/helpers/moderationQueueActionsMap'; + +import t from 'coral-framework/services/i18n'; const ActionButton = ({type = '', active, ...props}) => { const typeName = type.toLowerCase(); let text = menuActionsMap[type].text; - if (text === 'Approve' && active) { - text = 'Approved'; - } else if (text === 'Reject' && active) { - text = 'Rejected'; + if (text === 'approve' && active) { + text = 'approved'; + } else if (text === 'reject' && active) { + text = 'rejected'; } return ( + >{props.minimal ? '' : t(`modqueue.${text}`)} ); }; diff --git a/client/coral-admin/src/components/ActionsMenu.js b/client/coral-admin/src/components/ActionsMenu.js index eb173eb81..ff4a9c5b3 100644 --- a/client/coral-admin/src/components/ActionsMenu.js +++ b/client/coral-admin/src/components/ActionsMenu.js @@ -5,9 +5,7 @@ import cn from 'classnames'; import {findDOMNode} from 'react-dom'; import styles from './ActionsMenu.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; -const lang = new I18n(translations); +import t from 'coral-framework/services/i18n'; let count = 0; @@ -43,7 +41,7 @@ class ActionsMenu extends React.Component { onClick={this.syncOpenState} icon={this.props.icon} raised> - {lang.t('modqueue.actions')} + {t('modqueue.actions')} - {errorMessage && {lang.t(`errors.${errorMessage}`)}} + {errorMessage && {t(`error.${errorMessage}`)}}
×
-

{lang.t('modqueue.mod-faster')}

-

{lang.t('modqueue.try-these')}:

+

{t('modqueue.mod_faster')}

+

{t('modqueue.try_these')}:

    -
  • {lang.t('modqueue.approve')} t
  • -
  • {lang.t('modqueue.reject')} r
  • +
  • {t('modqueue.approve')} t
  • +
  • {t('modqueue.reject')} r
-

{lang.t('modqueue.view-more-shortcuts')} {lang.t('modqueue.shift-key')} + /

+

{t('modqueue.view_more_shortcuts')} {t('modqueue.shift_key')} + /

-

{lang.t('modqueue.shortcuts')}

+

{t('modqueue.shortcuts')}

{shortcuts.map((shortcut, i) => ( - + {Object.keys(shortcut.shortcuts).map((key) => ( - + ))} diff --git a/client/coral-admin/src/components/ModerationList.css b/client/coral-admin/src/components/ModerationList.css index 4d4f37c9a..2b5989191 100644 --- a/client/coral-admin/src/components/ModerationList.css +++ b/client/coral-admin/src/components/ModerationList.css @@ -189,6 +189,12 @@ width: 140px; } +.minimal { + width: 45px; + min-width: 0; + float: left; +} + .approve__active { box-shadow: none; color: white; diff --git a/client/coral-admin/src/components/ui/Drawer.js b/client/coral-admin/src/components/ui/Drawer.js index 2fcb5aafe..b49caf30a 100644 --- a/client/coral-admin/src/components/ui/Drawer.js +++ b/client/coral-admin/src/components/ui/Drawer.js @@ -2,8 +2,7 @@ import React, {PropTypes} from 'react'; import {Navigation, Drawer} from 'react-mdl'; import {IndexLink, Link} from 'react-router'; import styles from './Drawer.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../../translations.json'; +import t from 'coral-framework/services/i18n'; import {can} from 'coral-framework/services/perms'; const CoralDrawer = ({handleLogout, auth}) => ( @@ -15,7 +14,7 @@ const CoralDrawer = ({handleLogout, auth}) => ( className={styles.navLink} to="/admin/dashboard" activeClassName={styles.active}> - {lang.t('configure.dashboard')} + {t('configure.dashboard')} { can(auth.user, 'MODERATE_COMMENTS') && ( @@ -23,19 +22,19 @@ const CoralDrawer = ({handleLogout, auth}) => ( className={styles.navLink} to="/admin/moderate" activeClassName={styles.active}> - {lang.t('configure.moderate')} + {t('configure.moderate')} ) } - {lang.t('configure.stories')} + {t('configure.stories')} - {lang.t('configure.community')} + {t('configure.community')} { can(auth.user, 'UPDATE_CONFIG') && @@ -44,7 +43,7 @@ const CoralDrawer = ({handleLogout, auth}) => ( className={styles.navLink} to="/admin/configure" activeClassName={styles.active}> - {lang.t('configure.configure')} + {t('configure.configure')} ) } @@ -60,6 +59,4 @@ CoralDrawer.propTypes = { restricted: PropTypes.bool // hide app elements from a logged out user }; -const lang = new I18n(translations); - export default CoralDrawer; diff --git a/client/coral-admin/src/components/ui/Header.css b/client/coral-admin/src/components/ui/Header.css index eba94d892..7cb68ecbf 100644 --- a/client/coral-admin/src/components/ui/Header.css +++ b/client/coral-admin/src/components/ui/Header.css @@ -73,6 +73,8 @@ font-weight: 500; background-color: transparent; transition: background-color 200ms; + font-weight: 100; + letter-spacing: .8; &:hover { background-color: #232323; @@ -80,6 +82,7 @@ &.active { background-color: #232323; + font-weight: 400; } } diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index f7ba83491..bbc78a321 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -2,8 +2,7 @@ import React, {PropTypes} from 'react'; import {Navigation, Header, IconButton, MenuItem, Menu} from 'react-mdl'; import {Link, IndexLink} from 'react-router'; import styles from './Header.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../../translations.json'; +import t from 'coral-framework/services/i18n'; import {Logo} from './Logo'; import {can} from 'coral-framework/services/perms'; @@ -23,7 +22,7 @@ const CoralHeader = ({ className={styles.navLink} to="/admin/dashboard" activeClassName={styles.active}> - {lang.t('configure.dashboard')} + {t('configure.dashboard')} { can(auth.user, 'MODERATE_COMMENTS') && ( @@ -32,7 +31,7 @@ const CoralHeader = ({ className={styles.navLink} to="/admin/moderate" activeClassName={styles.active}> - {lang.t('configure.moderate')} + {t('configure.moderate')} ) } @@ -41,14 +40,14 @@ const CoralHeader = ({ className={styles.navLink} to="/admin/stories" activeClassName={styles.active}> - {lang.t('configure.stories')} + {t('configure.stories')} - {lang.t('configure.community')} + {t('configure.community')} { can(auth.user, 'UPDATE_CONFIG') && ( @@ -57,7 +56,7 @@ const CoralHeader = ({ className={styles.navLink} to="/admin/configure" activeClassName={styles.active}> - {lang.t('configure.configure')} + {t('configure.configure')} ) } @@ -71,8 +70,8 @@ const CoralHeader = ({
- showShortcuts(true)}>{lang.t('configure.shortcuts')} - {lang.t('configure.sign-out')} + showShortcuts(true)}>{t('configure.shortcuts')} + {t('configure.sign_out')}
@@ -91,6 +90,4 @@ CoralHeader.propTypes = { handleLogout: PropTypes.func.isRequired }; -const lang = new I18n(translations); - export default CoralHeader; diff --git a/client/coral-admin/src/constants/moderation.js b/client/coral-admin/src/constants/moderation.js index b960d95a2..374de616a 100644 --- a/client/coral-admin/src/constants/moderation.js +++ b/client/coral-admin/src/constants/moderation.js @@ -7,3 +7,8 @@ export const SHOW_SUSPEND_USER_DIALOG = 'SHOW_SUSPEND_USER_DIALOG'; export const HIDE_SUSPEND_USER_DIALOG = 'HIDE_SUSPEND_USER_DIALOG'; export const VIEW_USER_DETAIL = 'VIEW_USER_DETAIL'; export const HIDE_USER_DETAIL = 'HIDE_USER_DETAIL'; +export const SET_SORT_ORDER = 'MODERATION_SET_SORT_ORDER'; +export const CHANGE_USER_DETAIL_STATUSES = 'CHANGE_USER_DETAIL_STATUSES'; +export const SELECT_USER_DETAIL_COMMENT = 'SELECT_USER_DETAIL_COMMENT'; +export const UNSELECT_USER_DETAIL_COMMENT = 'UNSELECT_USER_DETAIL_COMMENT'; +export const CLEAR_USER_DETAIL_SELECTIONS = 'CLEAR_USER_DETAIL_SELECTIONS'; diff --git a/client/coral-admin/src/containers/Community/Loading.js b/client/coral-admin/src/containers/Community/Loading.js deleted file mode 100644 index beda271eb..000000000 --- a/client/coral-admin/src/containers/Community/Loading.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; - -const Loading = () => ( -

Loading results

-); - -export default Loading; diff --git a/client/coral-admin/src/containers/Community/Table.js b/client/coral-admin/src/containers/Community/Table.js deleted file mode 100644 index 5ee8e7f92..000000000 --- a/client/coral-admin/src/containers/Community/Table.js +++ /dev/null @@ -1,81 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import {SelectField, Option} from 'react-mdl-selectfield'; -import styles from './Community.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../../translations'; -import {setRole, setCommenterStatus} from '../../actions/community'; - -const lang = new I18n(translations); - -class Table extends Component { - - constructor (props) { - super(props); - this.onRoleChange = this.onRoleChange.bind(this); - } - - onRoleChange (id, role) { - this.props.dispatch(setRole(id, role)); - } - - onCommenterStatusChange (id, status) { - this.props.dispatch(setCommenterStatus(id, status)); - } - - render () { - const {headers, commenters, onHeaderClickHandler} = this.props; - - return ( -
{lang.t(shortcut.title)}{t(shortcut.title)}
{key}{lang.t(shortcut.shortcuts[key])}{t(shortcut.shortcuts[key])}
- - - {headers.map((header, i) =>( - - ))} - - - - {commenters.map((row, i)=> ( - - - - - - - ))} - -
onHeaderClickHandler({field: header.field})}> - {header.title} -
- {row.username} - {row.profiles.map(({id}) => id)} - - {row.created_at} - - this.onCommenterStatusChange(row.id, status)}> - - - - - this.onRoleChange(row.id, role)}> - - - - - -
- ); - } -} - -export default connect((state) => ({commenters: state.community.get('accounts')}))(Table); diff --git a/client/coral-admin/src/containers/Dashboard/Dashboard.js b/client/coral-admin/src/containers/Dashboard/Dashboard.js deleted file mode 100644 index e764bf596..000000000 --- a/client/coral-admin/src/containers/Dashboard/Dashboard.js +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import styles from './Dashboard.css'; -import {compose} from 'react-apollo'; -import {connect} from 'react-redux'; -import {getMetrics} from 'coral-admin/src/graphql/queries'; -import FlagWidget from './FlagWidget'; -import ActivityWidget from './ActivityWidget'; -import CountdownTimer from 'coral-admin/src/components/CountdownTimer'; - -import {Spinner} from 'coral-ui'; - -class Dashboard extends React.Component { - - reloadData = () => { - this.props.data.refetch(); - } - - render () { - - if (this.props.data && this.props.data.loading) { - return ; - } - - const {data: {assetsByActivity, assetsByFlag}} = this.props; - - return ( -
- -
- - -
-
- ); - } -} - -const mapStateToProps = (state) => { - return { - settings: state.settings.toJS(), - moderation: state.moderation.toJS() - }; -}; - -export default compose( - connect(mapStateToProps), - getMetrics -)(Dashboard); diff --git a/client/coral-admin/src/containers/Install/InstallContainer.js b/client/coral-admin/src/containers/Install/InstallContainer.js deleted file mode 100644 index 67f266123..000000000 --- a/client/coral-admin/src/containers/Install/InstallContainer.js +++ /dev/null @@ -1,101 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import styles from './style.css'; -import {Wizard, WizardNav} from 'coral-ui'; -import Layout from 'coral-admin/src/components/ui/Layout'; - -import { - goToStep, - nextStep, - submitUser, - checkInstall, - previousStep, - finishInstall, - submitSettings, - updateUserFormData, - updateSettingsFormData, - updatePermittedDomains -} from '../../actions/install'; - -import InitialStep from './components/Steps/InitialStep'; -import AddOrganizationName from './components/Steps/AddOrganizationName'; -import CreateYourAccount from './components/Steps/CreateYourAccount'; -import PermittedDomainsStep from './components/Steps/PermittedDomainsStep'; -import FinalStep from './components/Steps/FinalStep'; - -class InstallContainer extends Component { - componentDidMount() { - const {checkInstall} = this.props; - checkInstall(() => { - this.context.router.push('/admin'); - }); - } - - render() { - const {install} = this.props; - - return ( - -
- { - !install.alreadyInstalled ? ( -
-

Welcome to the Coral Project

- { install.step !== 0 ? : null } - - - - - - - -
- ) : ( -
Talk is already installed
- ) - } -
-
- ); - } -} - -InstallContainer.contextTypes = { - router: React.PropTypes.object -}; - -const mapStateToProps = (state) => ({ - install: state.install.toJS() -}); - -const mapDispatchToProps = (dispatch) => ({ - nextStep: () => dispatch(nextStep()), - goToStep: (step) => dispatch(goToStep(step)), - previousStep: () => dispatch(previousStep()), - finishInstall: () => dispatch(finishInstall()), - checkInstall: (next) => dispatch(checkInstall(next)), - handleDomainsChange: (value) => { - dispatch(updatePermittedDomains(value)); - }, - handleSettingsChange: (e) => { - const {name, value} = e.currentTarget; - dispatch(updateSettingsFormData(name, value)); - }, - handleUserChange: (e) => { - const {name, value} = e.currentTarget; - dispatch(updateUserFormData(name, value)); - }, - handleSettingsSubmit: (e) => { - e.preventDefault(); - dispatch(submitSettings()); - }, - handleUserSubmit: (e) => { - e.preventDefault(); - dispatch(submitUser()); - } -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(InstallContainer); diff --git a/client/coral-admin/src/containers/Install/components/Steps/InitialStep.js b/client/coral-admin/src/containers/Install/components/Steps/InitialStep.js deleted file mode 100644 index 18b49ce64..000000000 --- a/client/coral-admin/src/containers/Install/components/Steps/InitialStep.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import styles from './style.css'; -import {Button} from 'coral-ui'; - -const lang = new I18n(translations); -import translations from '../../translations.json'; -import I18n from 'coral-framework/modules/i18n/i18n'; - -const InitialStep = (props) => { - const {nextStep} = props; - return ( -
-

{lang.t('INITIAL.DESCRIPTION')}

- -
- ); -}; - -export default InitialStep; diff --git a/client/coral-admin/src/containers/Install/translations.json b/client/coral-admin/src/containers/Install/translations.json deleted file mode 100644 index e5b9f1542..000000000 --- a/client/coral-admin/src/containers/Install/translations.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "en": { - "INITIAL" : { - "DESCRIPTION": "Let's set up your Talk community in just a few short steps.", - "SUBMIT": "Get Started" - }, - "ADD_ORGANIZATION": { - "DESCRIPTION": "Please tell us the name of your organization. This will appear in emails when inviting new team members.", - "LABEL": "Organization Name", - "SAVE": "Save" - }, - "CREATE": { - "EMAIL": "Email address", - "USERNAME": "Username", - "PASSWORD": "Password", - "CONFIRM_PASSWORD": "Confirm Password", - "SAVE": "Save" - }, - "PERMITTED_DOMAINS": { - "TITLE": "Permitted domains", - "DESCRIPTION": "Enter the domains you would like to permit for Talk, e.g. your local, staging and production environments (ex. localhost:3000, staging.domain.com, domain.com).", - "SUBMIT": "Finish install" - }, - "FINAL": { - "DESCRIPTION": "Thanks for installing Talk! We sent an email to verify your email address. While you finish setting up the account, you can start engaging with your readers now.", - "LAUNCH": "Launch Talk", - "CLOSE": "Close this Installer" - } - }, - "es": { - "INITIAL" : { - "DESCRIPTION": "Configuremos tu comunidad de Talk en sólo algunos pasos.", - "SUBMIT": "Empezá!" - }, - "ADD_ORGANIZATION": { - "DESCRIPTION": "Por favor, dinos el nombre de tu organización. Este aparecerá en los emails cuando invites nuevos miembros.", - "LABEL": "Nombre de la Organización", - "SAVE": "Guardar" - }, - "CREATE": { - "EMAIL": "Dirección de E-Mail", - "USERNAME": "Usuario", - "PASSWORD": "Contraseña", - "CONFIRM_PASSWORD": "Confirmar contraseña", - "SAVE": "Guardar" - }, - "PERMITTED_DOMAINS": { - "TITLE": "Dominios Permitidos", - "DESCRIPTION": "Agrega dominios permitidos a Talk, e.g. tu localhost, staging y ambientes de production (ex. localhost:3000, staging.domain.com, domain.com).", - "SUBMIT": "Finalizar instalación" - }, - "FINAL": { - "DESCRIPTION": "Gracias por instalar Talk! Te enviamos un email para verificar tu identidad. Mientras se termina de configurar la cuenta, ya puedes empezar a interactuar con tus lectores", - "LAUNCH": "Lanzar Talk", - "CLOSE": "Cerrar este instalador" - } - } -} diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/Layout.js similarity index 100% rename from client/coral-admin/src/containers/LayoutContainer.js rename to client/coral-admin/src/containers/Layout.js diff --git a/client/coral-admin/src/containers/ModerationQueue/UserDetail.css b/client/coral-admin/src/containers/ModerationQueue/UserDetail.css deleted file mode 100644 index e37971ada..000000000 --- a/client/coral-admin/src/containers/ModerationQueue/UserDetail.css +++ /dev/null @@ -1,41 +0,0 @@ -.copyButton { - float: right; - top: -10px; -} - -.memberSince { - clear: both; -} - -.small { - color: #aaa; -} - -.stats { - display: flex; - - .stat { - margin: 0 4px 12px; - } - - .stat:last-child { - margin-right: 0; - } - - p { - margin: 0; - } - - .stat p:first-child { - font-weight: bold; - } -} - -.profileEmail { - border: none; - background-color: transparent; - font-size: 16px; - position: absolute; - width: 100%; - outline: none; -} diff --git a/client/coral-admin/src/containers/ModerationQueue/UserDetail.js b/client/coral-admin/src/containers/ModerationQueue/UserDetail.js deleted file mode 100644 index d9b85a060..000000000 --- a/client/coral-admin/src/containers/ModerationQueue/UserDetail.js +++ /dev/null @@ -1,74 +0,0 @@ -import React, {PropTypes} from 'react'; -import {Button, Drawer} from 'coral-ui'; -import styles from './UserDetail.css'; -import {compose} from 'react-apollo'; -import {getUserDetail} from 'coral-admin/src/graphql/queries'; -import Slot from 'coral-framework/components/Slot'; - -class UserDetail extends React.Component { - static propTypes = { - id: PropTypes.string.isRequired, - hideUserDetail: PropTypes.func.isRequired - } - - copyPermalink = () => { - this.profile.select(); - try { - document.execCommand('copy'); - } catch (e) { - - /* nothing */ - } - } - - render () { - const {data, hideUserDetail} = this.props; - - if (!('user' in data)) { - return null; - } - - const {user, totalComments, rejectedComments} = data; - const localProfile = user.profiles.find((p) => p.provider === 'local'); - let profile; - if (localProfile) { - profile = localProfile.id; - } - - let rejectedPercent = rejectedComments / totalComments; - if (rejectedPercent === Infinity || isNaN(rejectedPercent)) { - - // if totalComments is 0, you're dividing by zero, which is naughty - rejectedPercent = 0; - } - - return ( - -

{user.username}

- - {profile && this.profile = ref} value={profile} />} - -

Member since {new Date(user.created_at).toLocaleString()}

-
-

- Account summary -
Data represents the last six months of activity -

-
-
-

Total Comments

-

{totalComments}

-
-
-

Reject Rate

-

{`${(rejectedPercent).toFixed(1)}%`}

-
-
-
- ); - } -} - -export default compose( - getUserDetail -)(UserDetail); diff --git a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js deleted file mode 100644 index c90d3cd14..000000000 --- a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, {PropTypes} from 'react'; -import {Button} from 'coral-ui'; -import styles from './styles.css'; - -const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) => -
- { - showLoadMore && - } -
; - -LoadMore.propTypes = { - comments: PropTypes.array.isRequired, - loadMore: PropTypes.func.isRequired, - sort: PropTypes.oneOf(['CHRONOLOGICAL', 'REVERSE_CHRONOLOGICAL']).isRequired, - tab: PropTypes.oneOf(['rejected', 'premod', 'flagged', 'all', 'accepted']).isRequired, - assetId: PropTypes.string, - showLoadMore: PropTypes.bool.isRequired -}; - -export default LoadMore; diff --git a/client/coral-admin/src/containers/ModerationQueue/helpers/moderationQueueActionsMap.js b/client/coral-admin/src/containers/ModerationQueue/helpers/moderationQueueActionsMap.js deleted file mode 100644 index 09066831f..000000000 --- a/client/coral-admin/src/containers/ModerationQueue/helpers/moderationQueueActionsMap.js +++ /dev/null @@ -1,14 +0,0 @@ -export const actionsMap = { - PREMOD: ['APPROVE', 'REJECT'], - FLAGGED: ['APPROVE', 'REJECT'], - REJECTED: ['APPROVE', 'REJECTED'] -}; - -export const menuActionsMap = { - 'REJECT': {status: 'REJECTED', text: 'Reject', icon: 'close', key: 'r'}, - 'REJECTED': {status: 'REJECTED', text: 'Rejected', icon: 'close'}, - 'APPROVE': {status: 'ACCEPTED', text: 'Approve', icon: 'done', key: 't'}, - 'FLAGGED': {status: 'FLAGGED', text: 'Flag', icon: 'flag', filter: 'Untouched'}, - 'BAN': {status: 'BANNED', text: 'Ban User', icon: 'not interested'}, - '': {icon: 'done'} -}; diff --git a/client/coral-admin/src/containers/Streams/Stories.js b/client/coral-admin/src/containers/Streams/Stories.js deleted file mode 100644 index 7c7a65389..000000000 --- a/client/coral-admin/src/containers/Streams/Stories.js +++ /dev/null @@ -1,187 +0,0 @@ -import React, {Component} from 'react'; -import styles from './Stories.css'; -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 {Link} from 'react-router'; - -import {Pager, Icon} from 'coral-ui'; -import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl'; -import EmptyCard from 'coral-admin/src/components/EmptyCard'; - -const limit = 25; - -class Stories extends Component { - - state = { - search: '', - sort: 'desc', - filter: 'all', - statusMenus: {}, - timer: null, - page: 0 - } - - componentDidMount () { - this.props.fetchAssets(0, limit, '', this.state.sortBy); - } - - onSettingChange = (setting) => (e) => { - let options = this.state; - this.setState({[setting]: e.target.value}); - options[setting] = e.target.value; - this.props.fetchAssets(0, limit, options.search, options.sort, options.filter); - } - - onSearchChange = (e) => { - const search = e.target.value; - this.setState((prevState) => { - prevState.search = search; - clearTimeout(prevState.timer); - const fetchAssets = this.props.fetchAssets; - prevState.timer = setTimeout(() => { - fetchAssets(0, limit, search, this.state.sort, this.state.filter); - }, 350); - return prevState; - }); - } - - renderDate = (date) => { - const d = new Date(date); - return `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`; - } - - onStatusClick = (closeStream, id, statusMenuOpen) => () => { - if (statusMenuOpen) { - this.setState((prev) => { - prev.statusMenus[id] = false; - return prev; - }); - this.props.updateAssetState(id, closeStream ? Date.now() : null) - .then(() => { - const {search, sort, filter, page} = this.state; - this.props.fetchAssets(page, limit, search, sort, filter); - }); - } else { - this.setState((prev) => { - prev.statusMenus[id] = true; - return prev; - }); - } - } - - renderTitle = (title, {id}) => {title} - - renderStatus = (closedAt, {id}) => { - const closed = closedAt && new Date(closedAt).getTime() < Date.now(); - const statusMenuOpen = this.state.statusMenus[id]; - return
-
- {!statusMenuOpen && } - {closed ? lang.t('streams.closed') : lang.t('streams.open')} -
- { - statusMenuOpen && -
- {!closed ? lang.t('streams.closed') : lang.t('streams.open')} -
- } -
; - } - - onPageClick = (page) => { - this.setState({page}); - const {search, sort, filter} = this.state; - this.props.fetchAssets((page - 1) * limit, limit, search, sort, filter); - } - - render () { - const {search, sort, filter} = this.state; - const {assets} = this.props; - - const assetsIds = assets.ids.map((id) => assets.byId[id]); - - return ( -
-
-
- - -
-
{lang.t('streams.filter-streams')}
-
{lang.t('streams.stream-status')}
- - {lang.t('streams.all')} - {lang.t('streams.open')} - {lang.t('streams.closed')} - -
{lang.t('streams.sort-by')}
- - {lang.t('streams.newest')} - {lang.t('streams.oldest')} - -
- { - assetsIds.length - ?
- - {lang.t('streams.article')} - - {lang.t('streams.pubdate')} - - - {lang.t('streams.status')} - - - -
- : {lang.t('streams.empty_result')} - } -
- ); - } -} - -const mapStateToProps = ({assets}) => { - return { - assets: assets.toJS() - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - fetchAssets: (...args) => { - dispatch(fetchAssets.apply(this, args)); - }, - updateAssetState: (...args) => dispatch(updateAssetState.apply(this, args)) - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(Stories); - -const lang = new I18n(translations); diff --git a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql b/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql deleted file mode 100644 index 726194143..000000000 --- a/client/coral-admin/src/graphql/fragments/assetMetricsView.graphql +++ /dev/null @@ -1,12 +0,0 @@ -fragment metrics on Asset { - id - title - url - author - created_at - commentCount - action_summaries { - actionCount - actionableItemCount - } -} diff --git a/client/coral-admin/src/graphql/fragments/commentView.graphql b/client/coral-admin/src/graphql/fragments/commentView.graphql deleted file mode 100644 index 1f3b8b1d3..000000000 --- a/client/coral-admin/src/graphql/fragments/commentView.graphql +++ /dev/null @@ -1,31 +0,0 @@ -fragment commentView on Comment { - id - body - created_at - status - user { - id - name: username - status - } - asset { - id - title - url - } - action_summaries { - count - ... on FlagActionSummary { - reason - } - } - actions { - ... on FlagAction { - reason - message - user { - username - } - } - } -} diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js new file mode 100644 index 000000000..59a6d7c8f --- /dev/null +++ b/client/coral-admin/src/graphql/index.js @@ -0,0 +1,68 @@ +import {add} from 'coral-framework/services/graphqlRegistry'; +import update from 'immutability-helper'; +const queues = ['all', 'premod', 'flagged', 'accepted', 'rejected']; + +const extension = { + mutations: { + SetUserStatus: () => ({ + refetchQueries: ['CoralAdmin_Community'], + }), + RejectUsername: () => ({ + refetchQueries: ['CoralAdmin_Community'], + }), + SetCommentStatus: ({variables: {commentId, status}}) => ({ + updateQueries: { + CoralAdmin_Moderation: (prev) => { + const comment = queues.reduce((comment, queue) => { + return comment ? comment : prev[queue].nodes.find((c) => c.id === commentId); + }, null); + + let acceptedNodes = prev.accepted.nodes; + let acceptedCount = prev.acceptedCount; + let rejectedNodes = prev.rejected.nodes; + let rejectedCount = prev.rejectedCount; + + if (status !== comment.status) { + if (status === 'ACCEPTED') { + comment.status = 'ACCEPTED'; + acceptedCount++; + acceptedNodes = [comment, ...acceptedNodes]; + } + else if (status === 'REJECTED') { + comment.status = 'REJECTED'; + rejectedCount++; + rejectedNodes = [comment, ...rejectedNodes]; + } + } + + const premodNodes = prev.premod.nodes.filter((c) => c.id !== commentId); + const flaggedNodes = prev.flagged.nodes.filter((c) => c.id !== commentId); + const premodCount = premodNodes.length < prev.premod.nodes.length ? prev.premodCount - 1 : prev.premodCount; + const flaggedCount = flaggedNodes.length < prev.flagged.nodes.length ? prev.flaggedCount - 1 : prev.flaggedCount; + + if (status === 'REJECTED') { + acceptedNodes = prev.accepted.nodes.filter((c) => c.id !== commentId); + acceptedCount = acceptedNodes.length < prev.accepted.nodes.length ? prev.acceptedCount - 1 : prev.acceptedCount; + } + else if (status === 'ACCEPTED') { + rejectedNodes = prev.rejected.nodes.filter((c) => c.id !== commentId); + rejectedCount = rejectedNodes.length < prev.rejected.nodes.length ? prev.rejectedCount - 1 : prev.rejectedCount; + } + + return update(prev, { + premodCount: {$set: Math.max(0, premodCount)}, + flaggedCount: {$set: Math.max(0, flaggedCount)}, + acceptedCount: {$set: Math.max(0, acceptedCount)}, + rejectedCount: {$set: Math.max(0, rejectedCount)}, + premod: {nodes: {$set: premodNodes}}, + flagged: {nodes: {$set: flaggedNodes}}, + accepted: {nodes: {$set: acceptedNodes}}, + rejected: {nodes: {$set: rejectedNodes}}, + }); + } + } + }), + }, +}; + +add(extension); diff --git a/client/coral-admin/src/graphql/mutations/index.js b/client/coral-admin/src/graphql/mutations/index.js deleted file mode 100644 index 3aef68ebc..000000000 --- a/client/coral-admin/src/graphql/mutations/index.js +++ /dev/null @@ -1,153 +0,0 @@ -import {graphql} from 'react-apollo'; -import SET_USER_STATUS from './setUserStatus.graphql'; -import SET_COMMENT_STATUS from './setCommentStatus.graphql'; -import SUSPEND_USER from './suspendUser.graphql'; -import REJECT_USERNAME from './rejectUsername.graphql'; - -export const banUser = graphql(SET_USER_STATUS, { - props: ({mutate}) => ({ - banUser: ({userId}) => { - return mutate({ - variables: { - userId, - status: 'BANNED' - }, - refetchQueries: ['Users'] - }); - }}), -}); - -export const setUserStatus = graphql(SET_USER_STATUS, { - props: ({mutate}) => ({ - approveUser: ({userId}) => { - return mutate({ - variables: { - userId, - status: 'APPROVED' - }, - refetchQueries: ['Users'] - }); - } - }) -}); - -export const suspendUser = graphql(SUSPEND_USER, { - props: ({mutate}) => ({ - suspendUser: (input) => { - return mutate({ - variables: { - input, - }, - }); - } - }) -}); - -export const rejectUsername = graphql(REJECT_USERNAME, { - props: ({mutate}) => ({ - rejectUsername: (input) => { - return mutate({ - variables: { - input, - }, - refetchQueries: ['Users'] - }); - } - }) -}); - -const views = ['all', 'premod', 'flagged', 'accepted', 'rejected']; -export const setCommentStatus = graphql(SET_COMMENT_STATUS, { - props: ({mutate}) => ({ - acceptComment: ({commentId}) => { - return mutate({ - variables: { - commentId, - status: 'ACCEPTED' - }, - updateQueries: { - ModQueue: (oldData) => { - const comment = views.reduce((comment, view) => { - return comment ? comment : oldData[view].find((c) => c.id === commentId); - }, null); - let accepted; - let acceptedCount = oldData.acceptedCount; - - // if the comment was already in the Approved queue, don't re-add it - if (comment.status === 'ACCEPTED') { - accepted = [...oldData.accepted]; - } else { - comment.status = 'ACCEPTED'; - acceptedCount++; - accepted = [comment, ...oldData.accepted]; - } - - const premod = oldData.premod.filter((c) => c.id !== commentId); - const flagged = oldData.flagged.filter((c) => c.id !== commentId); - const rejected = oldData.rejected.filter((c) => c.id !== commentId); - const premodCount = premod.length < oldData.premod.length ? oldData.premodCount - 1 : oldData.premodCount; - const flaggedCount = flagged.length < oldData.flagged.length ? oldData.flaggedCount - 1 : oldData.flaggedCount; - const rejectedCount = rejected.length < oldData.rejected.length ? oldData.rejectedCount - 1 : oldData.rejectedCount; - - return { - ...oldData, - premodCount: Math.max(0, premodCount), - flaggedCount: Math.max(0, flaggedCount), - acceptedCount: Math.max(0, acceptedCount), - rejectedCount: Math.max(0, rejectedCount), - premod, - flagged, - accepted, - rejected, - }; - } - } - }); - }, - rejectComment: ({commentId}) => { - return mutate({ - variables: { - commentId, - status: 'REJECTED' - }, - updateQueries: { - ModQueue: (oldData) => { - const comment = views.reduce((comment, view) => { - return comment ? comment : oldData[view].find((c) => c.id === commentId); - }, null); - let rejected; - let rejectedCount = oldData.rejectedCount; - - // if the item was already in the Rejected queue, don't put it in again - if (comment.status === 'REJECTED') { - rejected = oldData.rejected; - } else { - comment.status = 'REJECTED'; - rejectedCount++; - rejected = [comment, ...oldData.rejected]; - } - - const premod = oldData.premod.filter((c) => c.id !== commentId); - const flagged = oldData.flagged.filter((c) => c.id !== commentId); - const accepted = oldData.accepted.filter((c) => c.id !== commentId); - const premodCount = premod.length < oldData.premod.length ? oldData.premodCount - 1 : oldData.premodCount; - const flaggedCount = flagged.length < oldData.flagged.length ? oldData.flaggedCount - 1 : oldData.flaggedCount; - const acceptedCount = accepted.length < oldData.accepted.length ? oldData.acceptedCount - 1 : oldData.acceptedCount; - - return { - ...oldData, - premodCount: Math.max(0, premodCount), - flaggedCount: Math.max(0, flaggedCount), - acceptedCount: Math.max(0, acceptedCount), - rejectedCount: Math.max(0, rejectedCount), - premod, - flagged, - accepted, - rejected - }; - } - } - }); - } - }) -}); diff --git a/client/coral-admin/src/graphql/mutations/rejectUsername.graphql b/client/coral-admin/src/graphql/mutations/rejectUsername.graphql deleted file mode 100644 index c07887649..000000000 --- a/client/coral-admin/src/graphql/mutations/rejectUsername.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation rejectUsername($input: RejectUsernameInput!) { - rejectUsername(input: $input) { - errors { - translation_key - } - } -} diff --git a/client/coral-admin/src/graphql/mutations/setCommentStatus.graphql b/client/coral-admin/src/graphql/mutations/setCommentStatus.graphql deleted file mode 100644 index 7ff6173a8..000000000 --- a/client/coral-admin/src/graphql/mutations/setCommentStatus.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation setCommentStatus($commentId: ID!, $status: COMMENT_STATUS!){ - setCommentStatus(id: $commentId, status: $status) { - errors { - translation_key - } - } -} diff --git a/client/coral-admin/src/graphql/mutations/setUserStatus.graphql b/client/coral-admin/src/graphql/mutations/setUserStatus.graphql deleted file mode 100644 index 32fcf7e20..000000000 --- a/client/coral-admin/src/graphql/mutations/setUserStatus.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation setUserStatus($userId: ID!, $status: USER_STATUS!) { - setUserStatus(id: $userId, status: $status) { - errors { - translation_key - } - } -} diff --git a/client/coral-admin/src/graphql/mutations/suspendUser.graphql b/client/coral-admin/src/graphql/mutations/suspendUser.graphql deleted file mode 100644 index f6ab2426f..000000000 --- a/client/coral-admin/src/graphql/mutations/suspendUser.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation suspendUser($input: SuspendUserInput!) { - suspendUser(input: $input) { - errors { - translation_key - } - } -} diff --git a/client/coral-admin/src/graphql/queries/assetsQuery.graphql b/client/coral-admin/src/graphql/queries/assetsQuery.graphql deleted file mode 100644 index 37950692d..000000000 --- a/client/coral-admin/src/graphql/queries/assetsQuery.graphql +++ /dev/null @@ -1,6 +0,0 @@ -query Assets { - assets { - id - title - } -} diff --git a/client/coral-admin/src/graphql/queries/getQueueCounts.graphql b/client/coral-admin/src/graphql/queries/getQueueCounts.graphql deleted file mode 100644 index 4a7c00d87..000000000 --- a/client/coral-admin/src/graphql/queries/getQueueCounts.graphql +++ /dev/null @@ -1,22 +0,0 @@ -query Counts ($asset_id: ID) { - allCount: commentCount(query: { - asset_id: $asset_id - }) - acceptedCount: commentCount(query: { - statuses: [ACCEPTED], - asset_id: $asset_id - }) - premodCount: commentCount(query: { - statuses: [PREMOD], - asset_id: $asset_id - }) - rejectedCount: commentCount(query: { - statuses: [REJECTED], - asset_id: $asset_id - }) - flaggedCount: commentCount(query: { - action_type: FLAG, - asset_id: $asset_id, - statuses: [NONE, PREMOD] - }) -} diff --git a/client/coral-admin/src/graphql/queries/index.js b/client/coral-admin/src/graphql/queries/index.js deleted file mode 100644 index ae94d70c3..000000000 --- a/client/coral-admin/src/graphql/queries/index.js +++ /dev/null @@ -1,116 +0,0 @@ -import {graphql} from 'react-apollo'; - -import MOD_QUEUE_QUERY from './modQueueQuery.graphql'; -import MOD_QUEUE_LOAD_MORE from './loadMore.graphql'; -import MOD_USER_FLAGGED_QUERY from './modUserFlaggedQuery.graphql'; -import METRICS from './metricsQuery.graphql'; -import USER_DETAIL from './userDetail.graphql'; -import GET_QUEUE_COUNTS from './getQueueCounts.graphql'; - -export const modQueueQuery = graphql(MOD_QUEUE_QUERY, { - options: ({params: {id = null}}) => { - return { - variables: { - asset_id: id, - sort: 'REVERSE_CHRONOLOGICAL' - } - }; - }, - props: ({ownProps: {params: {id = null}}, data}) => ({ - data, - modQueueResort: modQueueResort(id, data.fetchMore), - loadMore: loadMore(data.fetchMore) - }) -}); - -export const getMetrics = graphql(METRICS, { - options: ({settings: {dashboardWindowStart, dashboardWindowEnd}}) => { - - return { - variables: { - from: dashboardWindowStart, - to: dashboardWindowEnd - } - }; - } -}); - -export const loadMore = (fetchMore) => ({limit = 10, cursor, sort, tab, asset_id}) => { - let variables = { - limit, - cursor, - sort, - asset_id - }; - switch(tab) { - case 'all': - variables.statuses = null; - break; - case 'accepted': - variables.statuses = ['ACCEPTED']; - break; - case 'premod': - variables.statuses = ['PREMOD']; - break; - case 'flagged': - variables.statuses = ['NONE', 'PREMOD']; - variables.action_type = 'FLAG'; - break; - case 'rejected': - variables.statuses = ['REJECTED']; - break; - } - return fetchMore({ - query: MOD_QUEUE_LOAD_MORE, - variables, - updateQuery: (oldData, {fetchMoreResult:{comments}}) => { - return { - ...oldData, - [tab]: [ - ...oldData[tab], - ...comments - ] - }; - } - }); -}; - -export const modUserFlaggedQuery = graphql(MOD_USER_FLAGGED_QUERY, { - options: ({params: {action_type = 'FLAG'}}) => { - return { - variables: { - action_type: action_type - } - }; - } -}); - -export const modQueueResort = (id, fetchMore) => (sort) => { - return fetchMore({ - query: MOD_QUEUE_QUERY, - variables: { - asset_id: id, - sort - }, - updateQuery: (oldData, {fetchMoreResult:{data}}) => data - }); -}; - -export const getUserDetail = graphql(USER_DETAIL, { - options: ({id}) => { - return { - variables: {author_id: id} - }; - } -}); - -export const getQueueCounts = graphql(GET_QUEUE_COUNTS, { - options: ({params: {id = null}}) => { - return { - pollInterval: 5000, - variables: { - asset_id: id - } - }; - } -}); diff --git a/client/coral-admin/src/graphql/queries/loadMore.graphql b/client/coral-admin/src/graphql/queries/loadMore.graphql deleted file mode 100644 index a42c7b2bf..000000000 --- a/client/coral-admin/src/graphql/queries/loadMore.graphql +++ /dev/null @@ -1,13 +0,0 @@ -#import "../fragments/commentView.graphql" - -query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) { - comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort, action_type: $action_type}) { - ...commentView - action_summaries { - count - ... on FlagActionSummary { - reason - } - } - } -} diff --git a/client/coral-admin/src/graphql/queries/metricsQuery.graphql b/client/coral-admin/src/graphql/queries/metricsQuery.graphql deleted file mode 100644 index 17fac3034..000000000 --- a/client/coral-admin/src/graphql/queries/metricsQuery.graphql +++ /dev/null @@ -1,10 +0,0 @@ -#import "../fragments/assetMetricsView.graphql" - -query Metrics ($from: Date!, $to: Date!) { - assetsByFlag: assetMetrics(from: $from, to: $to, sort: FLAG) { - ...metrics - } - assetsByActivity: assetMetrics(from: $from, to: $to, sort: ACTIVITY) { - ...metrics - } -} diff --git a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql b/client/coral-admin/src/graphql/queries/modQueueQuery.graphql deleted file mode 100644 index 6bfcf81ec..000000000 --- a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql +++ /dev/null @@ -1,68 +0,0 @@ -#import "../fragments/commentView.graphql" - -query ModQueue ($asset_id: ID, $sort: SORT_ORDER) { - all: comments(query: { - statuses: [NONE, PREMOD, ACCEPTED, REJECTED], - asset_id: $asset_id, - sort: $sort - }) { - ...commentView - } - accepted: comments(query: { - statuses: [ACCEPTED], - asset_id: $asset_id, - sort: $sort - }) { - ...commentView - } - premod: comments(query: { - statuses: [PREMOD], - asset_id: $asset_id, - sort: $sort - }) { - ...commentView - } - flagged: comments(query: { - action_type: FLAG, - asset_id: $asset_id, - statuses: [NONE, PREMOD], - sort: $sort - }) { - ...commentView - } - rejected: comments(query: { - statuses: [REJECTED], - asset_id: $asset_id, - sort: $sort - }) { - ...commentView - } - assets: assets { - id - title - url - } - allCount: commentCount(query: { - asset_id: $asset_id - }) - acceptedCount: commentCount(query: { - statuses: [ACCEPTED], - asset_id: $asset_id - }) - premodCount: commentCount(query: { - statuses: [PREMOD], - asset_id: $asset_id - }) - rejectedCount: commentCount(query: { - statuses: [REJECTED], - asset_id: $asset_id - }) - flaggedCount: commentCount(query: { - action_type: FLAG, - asset_id: $asset_id, - statuses: [NONE, PREMOD] - }) - settings { - organizationName - } -} diff --git a/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql b/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql deleted file mode 100644 index c99d94583..000000000 --- a/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql +++ /dev/null @@ -1,26 +0,0 @@ -query Users ($action_type: ACTION_TYPE) { - users (query:{action_type: $action_type}){ - id - username - status - roles - actions{ - id - created_at - ... on FlagAction { - reason - message - user { - id - username - } - } - } - action_summaries { - count - ... on FlagActionSummary { - reason - } - } - } -} diff --git a/client/coral-admin/src/graphql/queries/userDetail.graphql b/client/coral-admin/src/graphql/queries/userDetail.graphql deleted file mode 100644 index c375b80ff..000000000 --- a/client/coral-admin/src/graphql/queries/userDetail.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query UserDetail ($author_id: ID!) { - user(id: $author_id) { - id - username - created_at - profiles { - id - provider - } - } - totalComments: commentCount(query: {author_id: $author_id}) - rejectedComments: commentCount(query: {author_id: $author_id, statuses: [REJECTED]}) -} diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js index 9a532856e..8cd7685f0 100644 --- a/client/coral-admin/src/index.js +++ b/client/coral-admin/src/index.js @@ -8,6 +8,11 @@ import store from './services/store'; import App from './components/App'; import 'react-mdl/extra/material.js'; +import './graphql'; +import {loadPluginsTranslations, injectPluginsReducers} from 'coral-framework/helpers/plugins'; + +loadPluginsTranslations(); +injectPluginsReducers(); render( diff --git a/client/coral-admin/src/reducers/index.js b/client/coral-admin/src/reducers/index.js index 3036d6271..04c8b6590 100644 --- a/client/coral-admin/src/reducers/index.js +++ b/client/coral-admin/src/reducers/index.js @@ -13,5 +13,5 @@ export default { community, moderation, install, - config + config, }; diff --git a/client/coral-admin/src/reducers/moderation.js b/client/coral-admin/src/reducers/moderation.js index 4bcd82d47..1b95bdb90 100644 --- a/client/coral-admin/src/reducers/moderation.js +++ b/client/coral-admin/src/reducers/moderation.js @@ -1,4 +1,4 @@ -import {fromJS, Map} from 'immutable'; +import {fromJS, Map, Set} from 'immutable'; import * as actions from '../constants/moderation'; const initialState = fromJS({ @@ -8,8 +8,12 @@ const initialState = fromJS({ commentId: null, commentStatus: null, userDetailId: null, + userDetailActiveTab: 'all', + userDetailStatuses: ['NONE', 'ACCEPTED', 'REJECTED', 'PREMOD'], + userDetailSelectedIds: new Set(), banDialog: false, shortcutsNoteVisible: window.localStorage.getItem('coral:shortcutsNote') || 'show', + sortOrder: 'REVERSE_CHRONOLOGICAL', suspendUserDialog: { show: false, userId: null, @@ -63,7 +67,21 @@ export default function moderation (state = initialState, action) { case actions.VIEW_USER_DETAIL: return state.set('userDetailId', action.userId); case actions.HIDE_USER_DETAIL: - return state.set('userDetailId', null); + return state + .set('userDetailId', null) + .update('userDetailSelectedIds', (set) => set.clear()); + case actions.CLEAR_USER_DETAIL_SELECTIONS: + return state.update('userDetailSelectedIds', (set) => set.clear()); + case actions.CHANGE_USER_DETAIL_STATUSES: + return state + .set('userDetailActiveTab', action.tab) + .set('userDetailStatuses', action.statuses); + case actions.SELECT_USER_DETAIL_COMMENT: + return state.update('userDetailSelectedIds', (set) => set.add(action.id)); + case actions.UNSELECT_USER_DETAIL_COMMENT: + return state.update('userDetailSelectedIds', (set) => set.delete(action.id)); + case actions.SET_SORT_ORDER: + return state.set('sortOrder', action.order); default : return state; } diff --git a/client/coral-admin/src/containers/Community/components/ActionButton.js b/client/coral-admin/src/routes/Community/components/ActionButton.js similarity index 73% rename from client/coral-admin/src/containers/Community/components/ActionButton.js rename to client/coral-admin/src/routes/Community/components/ActionButton.js index ad5346ac5..67f16acad 100644 --- a/client/coral-admin/src/containers/Community/components/ActionButton.js +++ b/client/coral-admin/src/routes/Community/components/ActionButton.js @@ -1,8 +1,10 @@ import React from 'react'; -import styles from '../Community.css'; +import styles from './Community.css'; import BanUserButton from './BanUserButton'; import {Button} from 'coral-ui'; -import {menuActionsMap} from '../../../containers/ModerationQueue/helpers/moderationQueueActionsMap'; +import {menuActionsMap} from '../../Moderation/helpers/moderationQueueActionsMap'; + +import t from 'coral-framework/services/i18n'; const ActionButton = ({type = '', user, ...props}) => { if (type === 'BAN') { @@ -17,7 +19,7 @@ const ActionButton = ({type = '', user, ...props}) => { onClick={() => { type === 'APPROVE' ? props.approveUser({userId: user.id}) : props.showSuspendUserDialog({user: user}); }} - >{menuActionsMap[type].text} + >{t(`modqueue.${menuActionsMap[type].text}`)} ); }; diff --git a/client/coral-admin/src/containers/Community/components/BanUserButton.css b/client/coral-admin/src/routes/Community/components/BanUserButton.css similarity index 100% rename from client/coral-admin/src/containers/Community/components/BanUserButton.css rename to client/coral-admin/src/routes/Community/components/BanUserButton.css diff --git a/client/coral-admin/src/containers/Community/components/BanUserButton.js b/client/coral-admin/src/routes/Community/components/BanUserButton.js similarity index 74% rename from client/coral-admin/src/containers/Community/components/BanUserButton.js rename to client/coral-admin/src/routes/Community/components/BanUserButton.js index a54cdd322..cc4935204 100644 --- a/client/coral-admin/src/containers/Community/components/BanUserButton.js +++ b/client/coral-admin/src/routes/Community/components/BanUserButton.js @@ -2,9 +2,7 @@ import React, {PropTypes} from 'react'; import {Button, Icon} from 'coral-ui'; import styles from './BanUserButton.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; -const lang = new I18n(translations); +import t from 'coral-framework/services/i18n'; const BanUserButton = ({user, ...props}) => (
@@ -14,7 +12,7 @@ const BanUserButton = ({user, ...props}) => ( onClick={props.onClick} raised> - {lang.t('comment.ban_user')} + {t('comment.ban_user')}
); diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.css b/client/coral-admin/src/routes/Community/components/BanUserDialog.css similarity index 100% rename from client/coral-admin/src/containers/Community/components/BanUserDialog.css rename to client/coral-admin/src/routes/Community/components/BanUserDialog.css diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.js b/client/coral-admin/src/routes/Community/components/BanUserDialog.js similarity index 72% rename from client/coral-admin/src/containers/Community/components/BanUserDialog.js rename to client/coral-admin/src/routes/Community/components/BanUserDialog.js index affffcd54..26a885e8b 100644 --- a/client/coral-admin/src/containers/Community/components/BanUserDialog.js +++ b/client/coral-admin/src/routes/Community/components/BanUserDialog.js @@ -4,10 +4,7 @@ import styles from './BanUserDialog.css'; import Button from 'coral-ui/components/Button'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; - -const lang = new I18n(translations); +import t from 'coral-framework/services/i18n'; const BanUserDialog = ({open, handleClose, handleBanUser, user}) => ( ( open={open} onClose={handleClose} onCancel={handleClose} - title={lang.t('community.ban_user')}> + title={t('community.ban_user')}> ×
-

{lang.t('community.ban_user')}

+

{t('community.ban_user')}

-

{lang.t('community.are_you_sure', user.username)}

- {lang.t('community.note')} +

{t('community.are_you_sure', user.username)}

+ {t('community.note')}
diff --git a/client/coral-admin/src/containers/Community/Community.css b/client/coral-admin/src/routes/Community/components/Community.css similarity index 100% rename from client/coral-admin/src/containers/Community/Community.css rename to client/coral-admin/src/routes/Community/components/Community.css diff --git a/client/coral-admin/src/containers/Community/CommunityContainer.js b/client/coral-admin/src/routes/Community/components/Community.js similarity index 54% rename from client/coral-admin/src/containers/Community/CommunityContainer.js rename to client/coral-admin/src/routes/Community/components/Community.js index 7c0051477..f6544b0ac 100644 --- a/client/coral-admin/src/containers/Community/CommunityContainer.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -1,55 +1,26 @@ import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import {compose} from 'react-apollo'; - -import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries'; -import {banUser, setUserStatus, rejectUsername} from 'coral-admin/src/graphql/mutations'; - -import { - fetchAccounts, - updateSorting, - newPage, - showBanUserDialog, - hideBanUserDialog, - showSuspendUserDialog, - hideSuspendUserDialog -} from '../../actions/community'; - -import CommunityMenu from './components/CommunityMenu'; -import BanUserDialog from './components/BanUserDialog'; -import SuspendUserDialog from './components/SuspendUserDialog'; +import CommunityMenu from './CommunityMenu'; +import BanUserDialog from './BanUserDialog'; +import SuspendUserDialog from './SuspendUserDialog'; import People from './People'; import FlaggedAccounts from './FlaggedAccounts'; -class CommunityContainer extends Component { +export default class Community extends Component { - constructor(props) { - super(props); + state = { + searchValue: '', + timer: null + }; - this.state = { - searchValue: '', - timer: null - }; - - this.onKeyDownHandler = this.onKeyDownHandler.bind(this); - this.onSearchChange = this.onSearchChange.bind(this); - this.onHeaderClickHandler = this.onHeaderClickHandler.bind(this); - this.onNewPageHandler = this.onNewPageHandler.bind(this); - } - - componentWillMount() { - this.props.fetchAccounts({}); - } - - onKeyDownHandler(e) { + onKeyDownHandler = (e) => { if (e.key === 'Enter') { e.preventDefault(); this.search(); } } - onSearchChange(e) { + onSearchChange = (e) => { const value = e.target.value; this.setState((prevState) => { prevState.searchValue = value; @@ -62,6 +33,16 @@ class CommunityContainer extends Component { }); } + onHeaderClickHandler = (sort) => { + this.props.updateSorting(sort); + this.search(); + } + + onNewPageHandler = (page) => { + this.props.newPage(page); + this.search({page}); + } + search(query = {}) { const {community} = this.props; @@ -71,21 +52,10 @@ class CommunityContainer extends Component { asc: community.ascPeople, ...query }); - - } - - onHeaderClickHandler(sort) { - this.props.dispatch(updateSorting(sort)); - this.search(); - } - - onNewPageHandler(page) { - this.props.dispatch(newPage(page)); - this.search({page}); } getTabContent(searchValue, props) { - const {community, data} = props; + const {community, root: {users}} = props; const activeTab = props.route.path === ':id' ? 'flagged' : props.route.path; if (activeTab === 'people') { @@ -108,9 +78,7 @@ class CommunityContainer extends Component { return (
({ - community: state.community.toJS() -}); - -const mapDispatchToProps = (dispatch) => ({ - fetchAccounts: (query) => dispatch(fetchAccounts(query)), - showBanUserDialog: (user) => dispatch(showBanUserDialog(user)), - hideBanUserDialog: () => dispatch(hideBanUserDialog(false)), - showSuspendUserDialog: (user) => dispatch(showSuspendUserDialog(user)), - hideSuspendUserDialog: () => dispatch(hideSuspendUserDialog()) -}); - -export default compose( - connect(mapStateToProps, mapDispatchToProps), - modUserFlaggedQuery, - banUser, - setUserStatus, - rejectUsername -)(CommunityContainer); diff --git a/client/coral-admin/src/containers/Community/CommunityLayout.js b/client/coral-admin/src/routes/Community/components/CommunityLayout.js similarity index 100% rename from client/coral-admin/src/containers/Community/CommunityLayout.js rename to client/coral-admin/src/routes/Community/components/CommunityLayout.js diff --git a/client/coral-admin/src/containers/Community/components/CommunityMenu.js b/client/coral-admin/src/routes/Community/components/CommunityMenu.js similarity index 73% rename from client/coral-admin/src/containers/Community/components/CommunityMenu.js rename to client/coral-admin/src/routes/Community/components/CommunityMenu.js index 9a51798ec..b89e9350a 100644 --- a/client/coral-admin/src/containers/Community/components/CommunityMenu.js +++ b/client/coral-admin/src/routes/Community/components/CommunityMenu.js @@ -1,14 +1,9 @@ import React from 'react'; import styles from './styles.css'; - -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; - +import t from 'coral-framework/services/i18n'; import {Link} from 'react-router'; -const lang = new I18n(translations); - const CommunityMenu = () => { const flaggedPath = '/admin/community/flagged'; const peoplePath = '/admin/community/people'; @@ -17,10 +12,10 @@ const CommunityMenu = () => {
- {lang.t('community.flaggedaccounts')} + {t('community.flaggedaccounts')} - {lang.t('community.people')} + {t('community.people')}
diff --git a/client/coral-admin/src/containers/Community/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js similarity index 66% rename from client/coral-admin/src/containers/Community/FlaggedAccounts.js rename to client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index 692cef70d..df6368844 100644 --- a/client/coral-admin/src/containers/Community/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -1,25 +1,19 @@ import React from 'react'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; -const lang = new I18n(translations); - +import t from 'coral-framework/services/i18n'; import styles from './Community.css'; - -import Loading from './Loading'; import EmptyCard from 'coral-admin/src/components/EmptyCard'; -import User from './components/User'; +import User from './User'; const FlaggedAccounts = ({...props}) => { - const {commenters, isFetching} = props; - const hasResults = !isFetching && commenters && !!commenters.length; + const {commenters} = props; + const hasResults = commenters && !!commenters.length; // if (commenter.status === 'PENDING' && commenter.actions.length > 0) { return (
- { isFetching && } { hasResults ? commenters.map((commenter, index) => { @@ -34,7 +28,7 @@ const FlaggedAccounts = ({...props}) => { suspendUser={props.suspendUser} />; }) - : {lang.t('community.no-flagged-accounts')} + : {t('community.no_flagged_accounts')} }
diff --git a/client/coral-admin/src/containers/Community/People.js b/client/coral-admin/src/routes/Community/components/People.js similarity index 69% rename from client/coral-admin/src/containers/Community/People.js rename to client/coral-admin/src/routes/Community/components/People.js index e375bac38..464f3bc5b 100644 --- a/client/coral-admin/src/containers/Community/People.js +++ b/client/coral-admin/src/routes/Community/components/People.js @@ -1,29 +1,26 @@ import React from 'react'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-admin/src/translations.json'; import styles from './Community.css'; -import Table from './Table'; +import Table from '../containers/Table'; import {Pager, Icon} from 'coral-ui'; -import EmptyCard from '../../components/EmptyCard'; - -const lang = new I18n(translations); +import EmptyCard from '../../../components/EmptyCard'; +import t from 'coral-framework/services/i18n'; const tableHeaders = [ { - title: lang.t('community.username_and_email'), + title: t('community.username_and_email'), field: 'username' }, { - title: lang.t('community.account_creation_date'), + title: t('community.account_creation_date'), field: 'created_at' }, { - title: lang.t('community.status'), + title: t('community.status'), field: 'status' }, { - title: lang.t('community.newsroom_role'), + title: t('community.newsroom_role'), field: 'role' } ]; @@ -41,7 +38,7 @@ const People = ({commenters, searchValue, onSearchChange, ...props}) => { className={styles.searchBoxInput} value={searchValue} onChange={onSearchChange} - placeholder={lang.t('streams.search')} + placeholder={t('streams.search')} />
@@ -53,7 +50,7 @@ const People = ({commenters, searchValue, onSearchChange, ...props}) => { commenters={commenters} onHeaderClickHandler={props.onHeaderClickHandler} /> - : {lang.t('community.no-results')} + : {t('community.no_results')} } + title={t('suspenduser.suspend_user')}>
- {lang.t(stages[stage].title, lang.t('suspenduser.username'))} + {t(stages[stage].title, t('suspenduser.username'))}
- {lang.t(stages[stage].description, lang.t('suspenduser.username'))} + {t(stages[stage].description, t('suspenduser.username'))}
{ stage === 1 &&
-
{lang.t('suspenduser.write_message')}
+
{t('suspenduser.write_message')}