diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index c5a03132f..2c77ffce7 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -17,3 +17,17 @@ export const checkLogin = () => dispatch => { }) .catch(error => dispatch(checkLoginFailure(error))); }; + +// LogOut Actions + +const logOutRequest = () => ({type: actions.LOGOUT_REQUEST}); +const logOutSuccess = () => ({type: actions.LOGOUT_SUCCESS}); +const logOutFailure = () => ({type: actions.LOGOUT_FAILURE}); + +export const logout = () => dispatch => { + dispatch(logOutRequest()); + fetch(`${base}/auth`, getInit('DELETE')) + .then(handleResp) + .then(() => dispatch(logOutSuccess())) + .catch(error => dispatch(logOutFailure(error))); +}; diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js index f71730663..b71a63e39 100644 --- a/client/coral-admin/src/actions/settings.js +++ b/client/coral-admin/src/actions/settings.js @@ -1,3 +1,5 @@ +import {base, handleResp, getInit} from '../helpers/response'; + export const SETTINGS_LOADING = 'SETTINGS_LOADING'; export const SETTINGS_RECEIVED = 'SETTINGS_RECEIVED'; export const SETTINGS_FETCH_ERROR = 'SETTINGS_FETCH_ERROR'; @@ -8,34 +10,6 @@ export const SAVE_SETTINGS_LOADING = 'SAVE_SETTINGS_LOADING'; export const SAVE_SETTINGS_SUCCESS = 'SAVE_SETTINGS_SUCCESS'; export const SAVE_SETTINGS_FAILED = 'SAVE_SETTINGS_FAILED'; -const base = '/api/v1'; - -const getInit = (method, body) => { - const headers = new Headers({ - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }); - - const init = {method, headers}; - if (method.toLowerCase() !== 'get') { - init.body = JSON.stringify(body); - } - - return init; -}; - -const handleResp = res => { - if (res.status === 401) { - throw new Error('Not Authorized to make this request'); - } else if (res.status > 399) { - throw new Error('Error! Status ', res.status); - } else if (res.status === 204) { - return res.text(); - } else { - return res.json(); - } -}; - export const fetchSettings = () => dispatch => { dispatch({type: SETTINGS_LOADING}); fetch(`${base}/settings`, getInit('GET')) diff --git a/client/coral-admin/src/components/FullLoading.css b/client/coral-admin/src/components/FullLoading.css new file mode 100644 index 000000000..8d850d381 --- /dev/null +++ b/client/coral-admin/src/components/FullLoading.css @@ -0,0 +1,12 @@ +.layout { + max-width: 800px; + margin: 0 auto; +} + +.layout h1 { + font-size: 40px; +} + +.layout img { + width: 100%; +} diff --git a/client/coral-admin/src/components/FullLoading.js b/client/coral-admin/src/components/FullLoading.js new file mode 100644 index 000000000..dee584aed --- /dev/null +++ b/client/coral-admin/src/components/FullLoading.js @@ -0,0 +1,13 @@ +import React from 'react'; +import {Layout} from 'react-mdl'; +import styles from './FullLoading.css'; +import {CoralLogo} from 'coral-ui'; + +export const FullLoading = () => ( + +
+

Loading

+ +
+
+); diff --git a/client/coral-admin/src/components/ui/Header.css b/client/coral-admin/src/components/ui/Header.css index 3d4e7dc77..a0d7dd30d 100644 --- a/client/coral-admin/src/components/ui/Header.css +++ b/client/coral-admin/src/components/ui/Header.css @@ -1,6 +1,5 @@ .header { background: #505050; - overflow: hidden; } .header > div { @@ -14,8 +13,35 @@ background: #232323; } -.version { +.rightPanel { position: absolute; right: 0; - width: 50px; + width: 170px; +} + +.rightPanel ul { + list-style: none; + line-height: 38px; +} + +.rightPanel li { + display: inline-block; + float: right; + margin-left: 15px; +} + +.rightPanel .settings { + vertical-align: middle; + border-radius: 3px; + border: solid 1px #9e9e9e; + line-height: 10px; +} + +.rightPanel .settings > div { + position: relative; +} + +.rightPanel .settings:hover { + background: rgba(158, 158, 158, 0.69); + cursor: pointer; } diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index 7ba88d25a..e4d151d30 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -1,21 +1,36 @@ import React from 'react'; -import {Navigation, Header} from 'react-mdl'; +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 {Logo} from './Logo'; -export default () => ( +export default ({handleLogout}) => (
- {lang.t('configure.moderate')} - {lang.t('configure.community')} - {lang.t('configure.configure')} + {lang.t('configure.moderate')} + {lang.t('configure.community')} + {lang.t('configure.configure')} -
- {`v${process.env.VERSION}`} +
+
    +
  • +
    + + + Sign Out + +
    +
  • +
  • + {`v${process.env.VERSION}`} +
  • +
); diff --git a/client/coral-admin/src/components/ui/Layout.js b/client/coral-admin/src/components/ui/Layout.js index 3e1b9cf2d..46c7aa7fa 100644 --- a/client/coral-admin/src/components/ui/Layout.js +++ b/client/coral-admin/src/components/ui/Layout.js @@ -4,9 +4,9 @@ import Header from './Header'; import Drawer from './Drawer'; import styles from './Layout.css'; -export const Layout = ({children}) => ( +export const Layout = ({children, ...props}) => ( -
+
{children} diff --git a/client/coral-admin/src/components/ui/Logo.css b/client/coral-admin/src/components/ui/Logo.css index e764af627..f89bf3d5d 100644 --- a/client/coral-admin/src/components/ui/Logo.css +++ b/client/coral-admin/src/components/ui/Logo.css @@ -1,7 +1,9 @@ .logo h1 { color: #272727; font-size: 20px; - padding: 0 30px; + margin: 0; + line-height: 60px; + padding: 0 20px; } .logo span { @@ -13,6 +15,7 @@ .logo { background: #E5E5E5; + height: 100%; } diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/LayoutContainer.js index 5f3cb0cff..f263c33f5 100644 --- a/client/coral-admin/src/containers/LayoutContainer.js +++ b/client/coral-admin/src/containers/LayoutContainer.js @@ -1,37 +1,31 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {Layout} from '../components/ui/Layout'; -import {checkLogin} from '../actions/auth'; -import {NotFound} from '../components/NotFound'; +import {checkLogin, logout} from '../actions/auth'; +import {FullLoading} from '../components/FullLoading'; import {PermissionRequired} from '../components/PermissionRequired'; class LayoutContainer extends Component { componentWillMount () { - this.props.checkLogin(); + const {checkLogin} = this.props; + checkLogin(); } render () { - const {isAdmin, loggedIn} = this.props.auth; - - if (!loggedIn) { - return ; - } - - if (!isAdmin && loggedIn) { - return ; - } - - return ; + const {isAdmin, loggedIn, loadingUser} = this.props.auth; + if (loadingUser) { return ; } + if (!isAdmin) { return ; } + if (isAdmin && loggedIn) { return ; } + return ; } } -LayoutContainer.propTypes = {}; - const mapStateToProps = state => ({ auth: state.auth.toJS() }); const mapDispatchToProps = dispatch => ({ checkLogin: () => dispatch(checkLogin()), + handleLogout: () => dispatch(logout()) }); export default connect( diff --git a/client/coral-admin/src/reducers/auth.js b/client/coral-admin/src/reducers/auth.js index 59dccac5e..f897c1bae 100644 --- a/client/coral-admin/src/reducers/auth.js +++ b/client/coral-admin/src/reducers/auth.js @@ -9,19 +9,25 @@ const initialState = Map({ export default function auth (state = initialState, action) { switch (action.type) { + case actions.CHECK_LOGIN_REQUEST: + return state + .set('loadingUser', true); case actions.CHECK_LOGIN_FAILURE: return state .set('loggedIn', false) + .set('loadingUser', false) .set('user', null); case actions.CHECK_LOGIN_SUCCESS: return state .set('loggedIn', true) + .set('loadingUser', false) .set('isAdmin', action.isAdmin) .set('user', action.user); case actions.LOGOUT_SUCCESS: return state .set('loggedIn', false) - .set('user', null); + .set('user', null) + .set('isAdmin', false); default : return state; } diff --git a/client/coral-admin/src/services/talk-adapter.js b/client/coral-admin/src/services/talk-adapter.js index 361a6479e..eeb799452 100644 --- a/client/coral-admin/src/services/talk-adapter.js +++ b/client/coral-admin/src/services/talk-adapter.js @@ -1,3 +1,4 @@ +import {base, handleResp, getInit} from '../helpers/response'; /** * The adapter is a redux middleware that interecepts the actions that need @@ -7,9 +8,6 @@ * for the coral but also for wordpress comments, disqus and many more. */ -// Default headers for json payloads. -const jsonHeader = new Headers({'Content-Type': 'application/json'}); - // Intercept redux actions and act over the ones we are interested export default store => next => action => { @@ -35,11 +33,11 @@ export default store => next => action => { const fetchModerationQueueComments = store => Promise.all([ - fetch('/api/v1/queue/comments/pending'), - fetch('/api/v1/comments?status=rejected'), - fetch('/api/v1/comments?action_type=flag') + fetch(`${base}/queue/comments/pending`, getInit('GET')), + fetch(`${base}/comments?status=rejected`, getInit('GET')), + fetch(`${base}/comments?action_type=flag`, getInit('GET')) ]) -.then(res => Promise.all(res.map(r => r.json()))) +.then(res => Promise.all(res.map(handleResp))) .then(res => { res[2] = res[2].map(comment => { comment.flagged = true; return comment; }); return res.reduce((prev, curr) => prev.concat(curr), []); @@ -51,26 +49,22 @@ Promise.all([ // Update a comment. Now to update a comment we need to send back the whole object const updateComment = (store, comment) => { - fetch(`/api/v1/comments/${comment.get('id')}/status`, { - method: 'PUT', - headers: jsonHeader, - body: JSON.stringify({status: comment.get('status')}) - }) - .then(res => res.json()) + fetch(`${base}/comments/${comment.get('id')}/status`, getInit('PUT', {status: comment.get('status')})) + .then(handleResp) .then(res => store.dispatch({type: 'COMMENT_UPDATE_SUCCESS', res})) .catch(error => store.dispatch({type: 'COMMENT_UPDATE_FAILED', error})); }; // Create a new comment -const createComment = (store, name, comment) => -fetch('/api/v1/comments', { - method: 'POST', - body: JSON.stringify({ +const createComment = (store, name, comment) => { + const body = { status: 'Untouched', body: comment, name: name, createdAt: Date.now() - }) -}).then(res => res.json()) -.then(res => store.dispatch({type: 'COMMENT_CREATE_SUCCESS', comment: res})) -.catch(error => store.dispatch({type: 'COMMENT_CREATE_FAILED', error})); + }; + return fetch(`${base}/comments`, getInit('POST', body)) + .then(handleResp) + .then(res => store.dispatch({type: 'COMMENT_CREATE_SUCCESS', comment: res})) + .catch(error => store.dispatch({type: 'COMMENT_CREATE_FAILED', error})); +}; diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index f922280c1..4af6dd1b1 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -104,20 +104,18 @@ export function getStream (assetUrl) { .then((json) => { /* Add items to the store */ - const itemTypes = Object.keys(json); - for (let i = 0; i < itemTypes.length; i++ ) { - if (itemTypes[i] === 'actions') { - for (let j = 0; j < json[itemTypes[i]].length; j++ ) { - let action = json[itemTypes[i]][j]; + Object.keys(json).forEach(type => { + if (type === 'actions') { + json[type].forEach(action => { action.id = `${action.action_type}_${action.item_id}`; dispatch(addItem(action, 'actions')); - } + }); } else { - for (let j = 0; j < json[itemTypes[i]].length; j++ ) { - dispatch(addItem(json[itemTypes[i]][j], itemTypes[i])); - } + json[type].forEach(item => { + dispatch(addItem(item, type)); + }); } - } + }); const assetId = json.assets[0].id; @@ -140,15 +138,14 @@ export function getStream (assetUrl) { dispatch(updateItem(assetId, 'comments', rels.rootComments, 'assets')); - const childKeys = Object.keys(rels.childComments); - for (let i = 0; i < childKeys.length; i++ ) { - dispatch(updateItem(childKeys[i], 'children', rels.childComments[childKeys[i]].reverse(), 'comments')); - } + Object.keys(rels.childComments).forEach(key => { + dispatch(updateItem(key, 'children', rels.childComments[key].reverse(), 'comments')); + }); /* Hydrate actions on comments */ - for (let i = 0; i < json.actions.length; i++ ) { - dispatch(updateItem(json.actions[i].item_id, json.actions[i].action_type, json.actions[i].id, 'comments')); - } + json.actions.forEach(action => { + dispatch(updateItem(action.item_id, action.action_type, action.id, 'comments')); + }); return (json); });