mirror of
https://github.com/wassname/talk.git
synced 2026-07-16 11:22:16 +08:00
Merge branch 'master' into kue-redis-fix
This commit is contained in:
@@ -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)));
|
||||
};
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.layout {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.layout h1 {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.layout img {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -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 = () => (
|
||||
<Layout fixedDrawer>
|
||||
<div className={styles.layout} >
|
||||
<h1>Loading</h1>
|
||||
<CoralLogo />
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}) => (
|
||||
<Header className={styles.header}>
|
||||
<Logo />
|
||||
<Navigation>
|
||||
<IndexLink className={styles.navLink} to="/admin" activeClassName={styles.active}>{lang.t('configure.moderate')}</IndexLink>
|
||||
<Link className={styles.navLink} to="/admin/community" activeClassName={styles.active}>{lang.t('configure.community')}</Link>
|
||||
<Link className={styles.navLink} to="/admin/configure" activeClassName={styles.active}>{lang.t('configure.configure')}</Link>
|
||||
<IndexLink className={styles.navLink} to="/admin"
|
||||
activeClassName={styles.active}>{lang.t('configure.moderate')}</IndexLink>
|
||||
<Link className={styles.navLink} to="/admin/community"
|
||||
activeClassName={styles.active}>{lang.t('configure.community')}</Link>
|
||||
<Link className={styles.navLink} to="/admin/configure"
|
||||
activeClassName={styles.active}>{lang.t('configure.configure')}</Link>
|
||||
</Navigation>
|
||||
<div className={styles.version}>
|
||||
{`v${process.env.VERSION}`}
|
||||
<div className={styles.rightPanel}>
|
||||
<ul>
|
||||
<li className={styles.settings}>
|
||||
<div>
|
||||
<IconButton name="settings" id="menu-settings"/>
|
||||
<Menu target="menu-settings" align="right">
|
||||
<MenuItem onClick={handleLogout}>Sign Out</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
{`v${process.env.VERSION}`}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
|
||||
@@ -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}) => (
|
||||
<LayoutMDL fixedDrawer>
|
||||
<Header />
|
||||
<Header {...props}/>
|
||||
<Drawer />
|
||||
<div className={styles.layout} >
|
||||
{children}
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 <NotFound />;
|
||||
}
|
||||
|
||||
if (!isAdmin && loggedIn) {
|
||||
return <PermissionRequired />;
|
||||
}
|
||||
|
||||
return <Layout {...this.props} />;
|
||||
const {isAdmin, loggedIn, loadingUser} = this.props.auth;
|
||||
if (loadingUser) { return <FullLoading />; }
|
||||
if (!isAdmin) { return <PermissionRequired />; }
|
||||
if (isAdmin && loggedIn) { return <Layout {...this.props} />; }
|
||||
return <FullLoading />;
|
||||
}
|
||||
}
|
||||
|
||||
LayoutContainer.propTypes = {};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
checkLogin: () => dispatch(checkLogin()),
|
||||
handleLogout: () => dispatch(logout())
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}));
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user