User Settings, Restricted User, Banned User, Bio and User actions and Reducers

This commit is contained in:
Belen Curcio
2016-11-30 08:42:25 -03:00
parent 5485a446f3
commit bd704bf2ce
21 changed files with 313 additions and 180 deletions
+155 -155
View File
@@ -1,11 +1,18 @@
import React, {Component, PropTypes} from 'react';
import Pym from 'pym.js';
import {connect} from 'react-redux';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations.json';
const lang = new I18n(translations);
import {
itemActions,
Notification,
notificationActions,
authActions,
} from '../../coral-framework';
import {connect} from 'react-redux';
import CommentBox from '../../coral-plugin-commentbox/CommentBox';
import InfoBox from '../../coral-plugin-infobox/InfoBox';
import Content from '../../coral-plugin-commentcontent/CommentContent';
@@ -13,27 +20,25 @@ import PubDate from '../../coral-plugin-pubdate/PubDate';
import Count from '../../coral-plugin-comment-count/CommentCount';
import AuthorName from '../../coral-plugin-author-name/AuthorName';
import {ReplyBox, ReplyButton} from '../../coral-plugin-replies';
import Pym from 'pym.js';
import FlagButton from '../../coral-plugin-flags/FlagButton';
import LikeButton from '../../coral-plugin-likes/LikeButton';
import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton';
import SignInContainer from '../../coral-sign-in/containers/SignInContainer';
import UserBox from '../../coral-sign-in/components/UserBox';
import {TabBar, Tab, TabContent} from '../../coral-ui';
import {TabBar, Tab, TabContent, Spinner} from '../../coral-ui';
import SettingsContainer from '../../coral-settings/containers/SettingsContainer';
import RestrictedContent from '../../coral-framework/components/RestrictedContent';
const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions;
const {addNotification, clearNotification} = notificationActions;
const {logout} = authActions;
const mapStateToProps = (state) => {
return {
config: state.config.toJS(),
items: state.items.toJS(),
notification: state.notification.toJS(),
auth: state.auth.toJS()
};
};
const mapStateToProps = state => ({
config: state.config.toJS(),
items: state.items.toJS(),
notification: state.notification.toJS(),
auth: state.auth.toJS()
});
const mapDispatchToProps = (dispatch) => ({
addItem: (item, itemType) => dispatch(addItem(item, itemType)),
@@ -43,12 +48,9 @@ const mapDispatchToProps = (dispatch) => ({
addNotification: (type, text) => dispatch(addNotification(type, text)),
clearNotification: () => dispatch(clearNotification()),
postAction: (item, action, user, itemType) => dispatch(postAction(item, action, user, itemType)),
deleteAction: (item, action, user, itemType) => {
return dispatch(deleteAction(item, action, user, itemType));
},
appendItemArray: (item, property, value, addToFront, itemType) =>
dispatch(appendItemArray(item, property, value, addToFront, itemType)),
logout: () => dispatch(logout()),
deleteAction: (item, action, user, itemType) => dispatch(deleteAction(item, action, user, itemType)),
appendItemArray: (item, property, value, addToFront, itemType) => dispatch(appendItemArray(item, property, value, addToFront, itemType)),
logout: () => dispatch(logout())
});
class CommentStream extends Component {
@@ -94,12 +96,6 @@ class CommentStream extends Component {
comments: [],
url: 'http://coralproject.net'
}, 'asset', 'assetTest');
// Loading mock user
//this.props.postItem({name: 'Ban Ki-Moon'}, 'user', 'user_8989')
// .then((id) => {
// this.props.setLoggedInUser(id);
// });
}
// TODO: Replace teststream id with id from params
@@ -113,145 +109,149 @@ class CommentStream extends Component {
{
rootItem
? <div>
<TabBar onChange={this.changeTab} activeTab={activeTab}>
<Tab><Count id={rootItemId} items={this.props.items}/></Tab>
<Tab>Settings</Tab>
</TabBar>
<TabContent show={activeTab === 0}>
<div id="commentBox">
<InfoBox
content={this.props.config.infoBoxContent}
enable={this.props.config.infoBoxEnable}
/>
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
<CommentBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={rootItemId}
premod={this.props.config.moderation}
reply={false}
author={user}
/>
{!loggedIn && <SignInContainer />}
</div>
{
rootItem.comments && rootItem.comments.map((commentId) => {
const comment = comments[commentId];
return <div className="comment" key={commentId}>
<hr aria-hidden={true}/>
<AuthorName author={users[comment.author_id]}/>
<PubDate created_at={comment.created_at}/>
<Content body={comment.body}/>
<div className="commentActionsLeft">
<ReplyButton
updateItem={this.props.updateItem}
id={commentId}
showReply={comment.showReply}/>
<LikeButton
addNotification={this.props.addNotification}
id={commentId}
like={actions[comment.like]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
</div>
<div className="commentActionsRight">
<FlagButton
addNotification={this.props.addNotification}
id={commentId}
flag={actions[comment.flag]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
<PermalinkButton
comment_id={commentId}
asset_id={comment.asset_id}/>
</div>
<ReplyBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={rootItemId}
author={user}
parent_id={commentId}
premod={this.props.config.moderation}
showReply={comment.showReply}/>
{
comment.children &&
comment.children.map((replyId) => {
let reply = this.props.items.comments[replyId];
return <div className="reply" key={replyId}>
<hr aria-hidden={true}/>
<AuthorName author={users[reply.author_id]}/>
<PubDate created_at={reply.created_at}/>
<Content body={reply.body}/>
<div className="replyActionsLeft">
<ReplyButton
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
{/* Add to the restricted param a boolean if the user is suspended*/}
<RestrictedContent restricted={false} message={lang.t('suspendedAccountMsg')}>
<TabContent show={activeTab === 0}>
<div id="commentBox">
<InfoBox
content={this.props.config.infoBoxContent}
enable={this.props.config.infoBoxEnable}
/>
<CommentBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={rootItemId}
premod={this.props.config.moderation}
reply={false}
author={user}
/>
{!loggedIn && <SignInContainer />}
</div>
{
rootItem.comments && rootItem.comments.map(commentId => {
const comment = comments[commentId];
// This should be a return Comment Component
return <div className="comment" key={commentId}>
<hr aria-hidden={true}/>
<AuthorName author={users[comment.author_id]}/>
<PubDate created_at={comment.created_at}/>
<Content body={comment.body}/>
<div className="commentActionsLeft">
<ReplyButton
updateItem={this.props.updateItem}
id={commentId}
showReply={comment.showReply}/>
<LikeButton
addNotification={this.props.addNotification}
id={commentId}
like={actions[comment.like]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
</div>
<div className="commentActionsRight">
<FlagButton
addNotification={this.props.addNotification}
id={commentId}
flag={actions[comment.flag]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
<PermalinkButton
comment_id={commentId}
asset_id={comment.asset_id}/>
</div>
<ReplyBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={rootItemId}
author={user}
parent_id={commentId}
premod={this.props.config.moderation}
showReply={comment.showReply}/>
{
comment.children &&
comment.children.map((replyId) => {
let reply = this.props.items.comments[replyId];
// This should be a Reply Component
return <div className="reply" key={replyId}>
<hr aria-hidden={true}/>
<AuthorName author={users[reply.author_id]}/>
<PubDate created_at={reply.created_at}/>
<Content body={reply.body}/>
<div className="replyActionsLeft">
<ReplyButton
updateItem={this.props.updateItem}
id={replyId}
showReply={reply.showReply}/>
<LikeButton
addNotification={this.props.addNotification}
id={replyId}
like={this.props.items.actions[reply.like]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
</div>
<div className="replyActionsRight">
<FlagButton
addNotification={this.props.addNotification}
id={replyId}
flag={this.props.items.actions[reply.flag]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
<PermalinkButton
comment_id={reply.parent_id}
asset_id={rootItemId}
/>
</div>
<ReplyBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={replyId}
id={rootItemId}
author={user}
parent_id={commentId}
child_id={replyId}
premod={this.props.config.moderation}
showReply={reply.showReply}/>
<LikeButton
addNotification={this.props.addNotification}
id={replyId}
like={this.props.items.actions[reply.like]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
</div>
<div className="replyActionsRight">
<FlagButton
addNotification={this.props.addNotification}
id={replyId}
flag={this.props.items.actions[reply.flag]}
postAction={this.props.postAction}
deleteAction={this.props.deleteAction}
addItem={this.props.addItem}
updateItem={this.props.updateItem}
currentUser={this.props.auth.user}/>
<PermalinkButton
comment_id={reply.parent_id}
asset_id={rootItemId}
/>
</div>
<ReplyBox
addNotification={this.props.addNotification}
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
id={rootItemId}
author={user}
parent_id={commentId}
child_id={replyId}
premod={this.props.config.moderation}
showReply={reply.showReply}/>
</div>;
})
}
</div>;
})
}
<Notification
notifLength={4500}
clearNotification={this.props.clearNotification}
notification={this.props.notification}
/>
</TabContent>
<TabContent show={activeTab === 1}>
<SettingsContainer/>
</TabContent>
</div>;
})
}
</div>;
})
}
<Notification
notifLength={4500}
clearNotification={this.props.clearNotification}
notification={this.props.notification}
/>
</TabContent>
<TabContent show={activeTab === 1}>
<SettingsContainer loggedIn={loggedIn} user={user} />
</TabContent>
</RestrictedContent>
</div>
: 'Loading'
:
<Spinner/>
}
</div>;
}
@@ -79,6 +79,7 @@ hr {
.coral-plugin-commentbox-textarea {
flex: 1;
padding: 5px;
min-height: 100px;
}
.coral-plugin-commentbox-button-container {
+16
View File
@@ -0,0 +1,16 @@
import * as actions from '../constants/user';
import {base, handleResp, getInit} from '../helpers/response';
const saveBioRequest = () => ({type: actions.SAVE_BIO_REQUEST});
const saveBioSuccess = bio => ({type: actions.SAVE_BIO_SUCCESS, bio});
const saveBioFailure = error => ({type: actions.SAVE_BIO_FAILURE, error});
export const saveBio = (formData) => dispatch => {
dispatch(saveBioRequest());
fetch(`${base}/auth/local`, getInit('POST', formData))
.then(handleResp)
.then(({user}) => {
dispatch(saveBioSuccess(user));
})
.catch(error => dispatch(saveBioFailure(error)));
};
@@ -0,0 +1,4 @@
.message {
background: #D8D8D8;
padding: 25px;
}
@@ -0,0 +1,20 @@
import React from 'react';
import styles from './RestrictedContent.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations.json';
const lang = new I18n(translations);
export default ({children, restricted, message = lang.t('contentNotAvailable'), restrictedComp}) => {
if (restricted) {
return restrictedComp ? restrictedComp() : messageBox(message);
} else {
return (
<div>
{children}
</div>
);
}
};
const messageBox = (message) => <div className={styles.message}>{message}</div>;
+3
View File
@@ -0,0 +1,3 @@
export const SAVE_BIO_REQUEST = 'SAVE_BIO_REQUEST';
export const SAVE_BIO_SUCCESS = 'SAVE_BIO_SUCCESS';
export const SAVE_BIO_FAILURE = 'SAVE_BIO_FAILURE';
+20
View File
@@ -0,0 +1,20 @@
import {Map} from 'immutable';
import {FETCH_SIGNIN_SUCCESS} from '../constants/auth';
import * as actions from '../constants/user';
const initialState = Map({
bio: ''
});
export default function user (state = initialState, action) {
switch (action.type) {
case FETCH_SIGNIN_SUCCESS :
return state
.set('bio', action.user.bio);
case actions.SAVE_BIO_SUCCESS :
return state
.set('bio', action.bio);
default :
return state;
}
}
+5 -1
View File
@@ -1,5 +1,7 @@
{
"en": {
"contentNotAvailable": "This content is not available",
"suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information",
"error": {
"email": "Not a valid E-Mail",
"password": "Password must be at least 8 characters",
@@ -10,6 +12,8 @@
}
},
"es": {
"contentNotAvailable": "El contenido no se encuentra disponible",
"suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information",
"error": {
"email": "No es un email válido",
"password": "La contraseña debe tener por lo menos 8 caracteres",
@@ -19,4 +23,4 @@
"emailInUse": "Email address already in use"
}
}
}
}
+4 -4
View File
@@ -1,6 +1,7 @@
import React, {Component, PropTypes} from 'react';
import {I18n} from '../coral-framework';
import translations from './translations.json';
import {Button} from 'coral-ui';
const name = 'coral-plugin-commentbox';
@@ -75,12 +76,11 @@ class CommentBox extends Component {
</div>
<div className={`${name}-button-container`}>
{ author && (
<button
className={`${name}-button`}
style={styles && styles.button}
<Button
cStyle='darkGrey'
onClick={this.postComment}>
{lang.t('post')}
</button>
</Button>
)
}
</div>
+3 -3
View File
@@ -2,14 +2,14 @@ import React from 'react';
import styles from './Bio.css';
import {Button} from '../../coral-ui';
export default () => (
export default ({user, handleSaveBio}) => (
<div className={styles.bio}>
<h1>Bio</h1>
<p>Tell the community about yourself</p>
<textarea />
<textarea defaultValue={user.bio}/>
<div className={styles.actions}>
<Button cStyle='cancel' raised>Cancel</Button>
<Button cStyle='success'>Save Changes</Button>
<Button cStyle='success' onClick={handleSaveBio}>Save Changes</Button>
</div>
</div>
);
@@ -0,0 +1,15 @@
.message {
padding: 10px 0 20px;
letter-spacing: 0.1px;
font-size: 13px;
line-height: 33px;
}
.message a {
color: black;
font-weight: bold;
cursor: pointer;
margin: 0px;
padding-bottom: 2px;
border-bottom: solid 1px black;
}
@@ -0,0 +1,17 @@
import React from 'react';
import styles from './NotLoggedIn.css';
export default () => (
<div className={styles.message}>
<div>
<a>Sign In</a> to access Settings
</div>
<div>
From the Settings Page you can
<ul>
<li>See your comment history</li>
<li>Write a bio about yourself to display to the community</li>
</ul>
</div>
</div>
);
@@ -1,10 +1,10 @@
import React from 'react';
import styles from './SettingsHeader.css';
export default () => (
export default ({user}) => (
<div className={styles.header}>
<h1>Jackson</h1>
<h2>jackson_persona@gmail.com</h2>
<h1>{user.displayName}</h1>
<h2>{user.profiles.map(profile => profile.id)}</h2>
</div>
);
@@ -1,11 +1,13 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {TabBar, Tab} from '../../coral-ui';
import {TabBar, Tab, TabContent} from '../../coral-ui';
import Bio from '../components/Bio';
import CommentHistory from '../components/CommentHistory';
import SettingsHeader from '../components/SettingsHeader';
import NotLoggedIn from '../components/NotLoggedIn';
import RestrictedContent from 'coral-framework/components/RestrictedContent';
class SignInContainer extends Component {
constructor (props) {
@@ -29,18 +31,22 @@ class SignInContainer extends Component {
}
render() {
//const {embedStream} = this.props;
const {loggedIn} = this.props;
const {activeTab} = this.state;
return (
<div>
<SettingsHeader />
<RestrictedContent restricted={!loggedIn} restrictedComp={NotLoggedIn}>
<SettingsHeader {...this.props} />
<TabBar onChange={this.handleTabChange} activeTab={activeTab} cStyle='material'>
<Tab>All Comments (120)</Tab>
<Tab>Profile Settings</Tab>
</TabBar>
{ activeTab === 0 && <CommentHistory {...this.props}/> }
{ activeTab === 1 && <Bio {...this.props}/> }
</div>
<TabContent show={activeTab === 0}>
<CommentHistory {...this.props}/>
</TabContent>
<TabContent show={activeTab === 1}>
<Bio {...this.props} />
</TabContent>
</RestrictedContent>
);
}
}
@@ -49,10 +55,8 @@ const mapStateToProps = () => ({
});
const mapDispatchToProps = dispatch => ({
getBio: () => dispatch(),
handleSaveBio: () => dispatch(),
getHistory: () => dispatch(),
handleSaveChanges: () => dispatch(),
handleCancel: () => dispatch()
});
export default connect(
+5 -2
View File
@@ -1,12 +1,15 @@
import React from 'react';
import styles from './styles.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
const UserBox = ({className, user, logout, ...props}) => (
<div
className={`${styles.userBox} ${className}`}
className={`${styles.userBox} ${className ? className : ''}`}
{...props}
>
Signed in as <a>{user.displayName}</a>. Not you? <a onClick={logout}>Sign out</a>
{lang.t('signIn.loggedInAs')} <a>{user.displayName}</a>. {lang.t('signIn.notYou')} <a onClick={logout}>{lang.t('signIn.logout')}</a>
</div>
);
+9 -1
View File
@@ -103,10 +103,18 @@ input.error{
color: #B71C1C;
}
.userBox {
padding: 10px 0 20px;
letter-spacing: 0.1px;
}
.userBox a {
color: #2c69b6;
color: black;
font-weight: bold;
cursor: pointer;
margin: 0px;
padding-bottom: 2px;
border-bottom: solid 1px black;
}
.attention {
+4
View File
@@ -1,6 +1,8 @@
export default {
en: {
'signIn': {
notYou: 'Not you?',
loggedInAs: 'Logged in as',
facebookSignIn: 'Sign in with Facebook',
facebookSignUp: 'Sign up with Facebook',
logout: 'Logout',
@@ -24,6 +26,8 @@ export default {
},
es: {
'signIn': {
notYou: 'No eres tu?',
loggedInAs: 'Entraste como',
facebookSignIn: 'Entrar con Facebook',
facebookSignUp: 'Regístrate con Facebook',
logout: 'Salir',
+10
View File
@@ -67,6 +67,16 @@
background: #4f5c67;
}
.type--darkGrey {
color: white;
background: #696969;
}
.type--darkGrey:hover {
color: white;
background: #696969;
}
.full {
width: 100%;
margin: 0;
+1
View File
@@ -15,6 +15,7 @@
padding: 8px 10px;
margin-right: -1px;
user-select: none;
font-size: 13px;
}
.base li:hover {
+1
View File
@@ -5,3 +5,4 @@ export {default as TabBar} from './components/TabBar';
export {default as Tab} from './components/Tab';
export {default as TabContent} from './components/TabContent';
export {default as Button} from './components/Button';
export {default as Spinner} from './components/Spinner';
+3 -1
View File
@@ -74,7 +74,9 @@ const UserSchema = new mongoose.Schema({
// Roles provides an array of roles (as strings) that is associated with a
// user.
roles: [String]
roles: [String],
// User's bio
bio: String,
}, {
// This will ensure that we have proper timestamps available on this model.