mirror of
https://github.com/wassname/talk.git
synced 2026-09-09 11:38:08 +08:00
Merge branch 'master' of https://github.com/coralproject/talk into notification-room
This commit is contained in:
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import {Button, Icon} from 'react-mdl';
|
||||
import timeago from 'timeago.js';
|
||||
import styles from './CommentList.css';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
|
||||
// Render a single comment for the list
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {Layout, Navigation, Drawer, Header} from 'react-mdl';
|
||||
import {Link} from 'react-router';
|
||||
import styles from './Header.css';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
|
||||
// App header. If we add a navbar it should be here
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
import React from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {Navigation, Drawer} from 'react-mdl';
|
||||
import {Link} from 'react-router';
|
||||
import styles from './Header.css';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
|
||||
export default () => (
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {Navigation, Header} from 'react-mdl';
|
||||
import {Link} from 'react-router';
|
||||
import styles from './Header.css';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
|
||||
export default () => (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations.json';
|
||||
import {Grid, Cell} from 'react-mdl';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ 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/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../../translations';
|
||||
import {setRole} from '../../actions/community';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Icon
|
||||
} from 'react-mdl';
|
||||
import styles from './Configure.css';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
|
||||
class Configure extends React.Component {
|
||||
|
||||
@@ -5,7 +5,7 @@ import CommentList from 'components/CommentList';
|
||||
import {updateStatus} from 'actions/comments';
|
||||
import styles from './ModerationQueue.css';
|
||||
import key from 'keymaster';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations.json';
|
||||
|
||||
/*
|
||||
|
||||
@@ -17,10 +17,12 @@ 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';
|
||||
|
||||
const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions;
|
||||
const {addNotification, clearNotification} = notificationActions;
|
||||
const {setLoggedInUser} = authActions;
|
||||
const {logout} = authActions;
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
@@ -31,40 +33,21 @@ const mapStateToProps = (state) => {
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
addItem: (item, itemType) => {
|
||||
return dispatch(addItem(item, itemType));
|
||||
},
|
||||
updateItem: (id, property, value, itemType) => {
|
||||
return dispatch(updateItem(id, property, value, itemType));
|
||||
},
|
||||
postItem: (data, type, id) => {
|
||||
return dispatch(postItem(data, type, id));
|
||||
},
|
||||
getStream: (rootId) => {
|
||||
return dispatch(getStream(rootId));
|
||||
},
|
||||
addNotification: (type, text) => {
|
||||
return dispatch(addNotification(type, text));
|
||||
},
|
||||
clearNotification: () => {
|
||||
return dispatch(clearNotification());
|
||||
},
|
||||
setLoggedInUser: (user_id) => {
|
||||
return dispatch(setLoggedInUser(user_id));
|
||||
},
|
||||
postAction: (item, action, user, itemType) => {
|
||||
return dispatch(postAction(item, action, user, itemType));
|
||||
},
|
||||
deleteAction: (item, action, user, itemType) => {
|
||||
return dispatch(deleteAction(item, action, user, itemType));
|
||||
},
|
||||
appendItemArray: (item, property, value, addToFront, itemType) => {
|
||||
return dispatch(appendItemArray(item, property, value, addToFront, itemType));
|
||||
}
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
addItem: (item, itemType) => dispatch(addItem(item, itemType)),
|
||||
updateItem: (id, property, value, itemType) => dispatch(updateItem(id, property, value, itemType)),
|
||||
postItem: (data, type, id) => dispatch(postItem(data, type, id)),
|
||||
getStream: (rootId) => dispatch(getStream(rootId)),
|
||||
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())
|
||||
});
|
||||
|
||||
class CommentStream extends Component {
|
||||
|
||||
@@ -83,11 +66,25 @@ class CommentStream extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
if (Object.keys(this.props.items).length === 0) {
|
||||
// Loading mock asset
|
||||
this.props.postItem({
|
||||
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
|
||||
|
||||
const rootItemId = this.props.items.assets && Object.keys(this.props.items.assets)[0];
|
||||
const rootItem = this.props.items.assets && this.props.items.assets[rootItemId];
|
||||
const {loggedIn, user} = this.props.auth;
|
||||
return <div>
|
||||
{
|
||||
rootItem
|
||||
@@ -99,6 +96,7 @@ class CommentStream extends Component {
|
||||
<Count
|
||||
id={rootItemId}
|
||||
items={this.props.items}/>
|
||||
{loggedIn && <UserBox user={user} logout={this.props.logout} />}
|
||||
<CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
@@ -106,7 +104,10 @@ class CommentStream extends Component {
|
||||
updateItem={this.props.updateItem}
|
||||
id={rootItemId}
|
||||
premod={this.props.config.moderation}
|
||||
reply={false}/>
|
||||
reply={false}
|
||||
canPost={loggedIn}
|
||||
/>
|
||||
{!loggedIn && <SignInContainer />}
|
||||
</div>
|
||||
{
|
||||
rootItem.comments && rootItem.comments.map((commentId) => {
|
||||
|
||||
@@ -5,7 +5,6 @@ body {
|
||||
font-size: 12px;
|
||||
margin: 0px;
|
||||
padding: 0px 0px 50px 0px;
|
||||
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from './../translations';
|
||||
const lang = new I18n(translations);
|
||||
import * as actions from '../constants/auth';
|
||||
import {base, handleResp, getInit} from '../helpers/response';
|
||||
|
||||
// Dialog Actions
|
||||
export const showSignInDialog = () => ({type: actions.SHOW_SIGNIN_DIALOG});
|
||||
export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG});
|
||||
|
||||
export const changeView = view => dispatch =>
|
||||
dispatch({
|
||||
type: actions.CHANGE_VIEW,
|
||||
view
|
||||
});
|
||||
|
||||
export const cleanState = () => ({type: actions.CLEAN_STATE});
|
||||
|
||||
// Sign In Actions
|
||||
|
||||
const signInRequest = () => ({type: actions.FETCH_SIGNIN_REQUEST});
|
||||
const signInSuccess = (user) => ({type: actions.FETCH_SIGNIN_SUCCESS, user});
|
||||
const signInFailure = (error) => ({type: actions.FETCH_SIGNIN_FAILURE, error});
|
||||
|
||||
export const fetchSignIn = (formData) => dispatch => {
|
||||
dispatch(signInRequest());
|
||||
fetch(`${base}/auth/local`, getInit('POST', formData))
|
||||
.then(handleResp)
|
||||
.then(({user}) => {
|
||||
dispatch(hideSignInDialog());
|
||||
dispatch(signInSuccess(user));
|
||||
})
|
||||
.catch(() => dispatch(signInFailure(lang.t('error.emailPasswordError'))));
|
||||
};
|
||||
|
||||
// Sign In - Facebook
|
||||
|
||||
const signInFacebookRequest = () => ({type: actions.FETCH_SIGNIN_FACEBOOK_REQUEST});
|
||||
const signInFacebookSuccess = user => ({type: actions.FETCH_SIGNIN_FACEBOOK_SUCCESS, user});
|
||||
const signInFacebookFailure = error => ({type: actions.FETCH_SIGNIN_FACEBOOK_FAILURE, error});
|
||||
|
||||
export const fetchSignInFacebook = () => dispatch => {
|
||||
dispatch(signInFacebookRequest());
|
||||
window.open(
|
||||
`${base}/auth/facebook`,
|
||||
'Continue with Facebook',
|
||||
'menubar=0,resizable=0,width=500,height=500,top=200,left=500'
|
||||
);
|
||||
};
|
||||
|
||||
export const facebookCallback = (err, data) => dispatch => {
|
||||
if (err) {
|
||||
signInFacebookFailure(err);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dispatch(signInFacebookSuccess(JSON.parse(data)));
|
||||
dispatch(hideSignInDialog());
|
||||
} catch (err) {
|
||||
dispatch(signInFacebookFailure(err));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Sign Up Actions
|
||||
|
||||
const signUpRequest = () => ({type: actions.FETCH_SIGNUP_REQUEST});
|
||||
const signUpSuccess = user => ({type: actions.FETCH_SIGNUP_SUCCESS, user});
|
||||
const signUpFailure = error => ({type: actions.FETCH_SIGNUP_FAILURE, error});
|
||||
|
||||
export const fetchSignUp = formData => dispatch => {
|
||||
dispatch(signUpRequest());
|
||||
fetch(`${base}/user`, getInit('POST', formData))
|
||||
.then(handleResp)
|
||||
.then(({user}) => {
|
||||
dispatch(signUpSuccess(user));
|
||||
setTimeout(() =>{
|
||||
dispatch(changeView('SIGNIN'));
|
||||
}, 3000);
|
||||
})
|
||||
.catch(() => dispatch(signUpFailure(lang.t('error.emailInUse')))); // We need to inprove error handling. TODO (bc)
|
||||
};
|
||||
|
||||
// Forgot Password Actions
|
||||
|
||||
const forgotPassowordRequest = () => ({type: actions.FETCH_FORGOT_PASSWORD_REQUEST});
|
||||
const forgotPassowordSuccess = () => ({type: actions.FETCH_FORGOT_PASSWORD_SUCCESS});
|
||||
const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILURE});
|
||||
|
||||
export const fetchForgotPassword = () => dispatch => {
|
||||
dispatch(forgotPassowordRequest());
|
||||
fetch(`${base}/user/request-password-reset`, getInit('POST'))
|
||||
.then(handleResp)
|
||||
.then(() => dispatch(forgotPassowordSuccess()))
|
||||
.catch(error => dispatch(forgotPassowordFailure(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)));
|
||||
};
|
||||
|
||||
// LogOut Actions
|
||||
|
||||
export const validForm = () => ({type: actions.VALID_FORM});
|
||||
export const invalidForm = error => ({type: actions.INVALID_FORM, error});
|
||||
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
/* @flow */
|
||||
|
||||
import {fromJS} from 'immutable';
|
||||
|
||||
/**
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
/* Notification Actions */
|
||||
|
||||
export const ADD_NOTIFICATION = 'ADD_NOTIFICATION';
|
||||
export const CLEAR_NOTIFICATION = 'CLEAR_NOTIFICATION';
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
export const CHANGE_VIEW = 'CHANGE_VIEW';
|
||||
export const CLEAN_STATE = 'CLEAN_STATE';
|
||||
|
||||
export const SHOW_SIGNIN_DIALOG = 'SHOW_SIGNIN_DIALOG';
|
||||
export const HIDE_SIGNIN_DIALOG = 'HIDE_SIGNIN_DIALOG';
|
||||
|
||||
export const FETCH_SIGNUP_REQUEST = 'FETCH_SIGNUP_REQUEST';
|
||||
export const FETCH_SIGNUP_FAILURE = 'FETCH_SIGNUP_FAILURE';
|
||||
export const FETCH_SIGNUP_SUCCESS = 'FETCH_SIGNUP_SUCCESS';
|
||||
|
||||
export const FETCH_SIGNIN_REQUEST = 'FETCH_SIGNIN_REQUEST';
|
||||
export const FETCH_SIGNIN_FAILURE = 'FETCH_SIGNIN_FAILURE';
|
||||
export const FETCH_SIGNIN_SUCCESS = 'FETCH_SIGNIN_SUCCESS';
|
||||
|
||||
export const FETCH_SIGNIN_FACEBOOK_REQUEST = 'FETCH_SIGNIN_FACEBOOK_REQUEST';
|
||||
export const FETCH_SIGNIN_FACEBOOK_FAILURE = 'FETCH_SIGNIN_FACEBOOK_FAILURE';
|
||||
export const FETCH_SIGNIN_FACEBOOK_SUCCESS = 'FETCH_SIGNIN_FACEBOOK_SUCCESS';
|
||||
|
||||
export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST';
|
||||
export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS';
|
||||
export const FETCH_FORGOT_PASSWORD_FAILURE = 'FETCH_FORGOT_PASSWORD_FAILURE';
|
||||
|
||||
export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
|
||||
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
|
||||
export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
|
||||
|
||||
export const INVALID_FORM = 'INVALID_FORM';
|
||||
export const VALID_FORM = 'VALID_FORM';
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from './../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
export default {
|
||||
email: lang.t('error.email'),
|
||||
password: lang.t('error.password'),
|
||||
displayName: lang.t('error.displayName'),
|
||||
confirmPassword: lang.t('error.confirmPassword')
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
export const base = '/api/v1';
|
||||
|
||||
export const getInit = (method, body) => {
|
||||
let init = {
|
||||
method,
|
||||
headers: new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
}),
|
||||
credentials: 'same-origin'
|
||||
};
|
||||
|
||||
if (method.toLowerCase() !== 'get') {
|
||||
init.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
return init;
|
||||
};
|
||||
|
||||
export 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();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
email: email => (/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)),
|
||||
password: pass => (/^(?=.{8,}).*$/.test(pass)),
|
||||
confirmPassword: () => true,
|
||||
displayName: displayName => (/^(?=.{3,}).*$/.test(displayName))
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
import Notification from './notification/Notification';
|
||||
import store from './store/store';
|
||||
import {fetchConfig} from './store/actions/config';
|
||||
import * as itemActions from './store/actions/items';
|
||||
import I18n from './i18n/i18n';
|
||||
import * as notificationActions from './store/actions/notification';
|
||||
import * as authActions from './store/actions/auth';
|
||||
import Notification from './modules/notification/Notification';
|
||||
import store from './store';
|
||||
import {fetchConfig} from './actions/config';
|
||||
import * as itemActions from './actions/items';
|
||||
import I18n from './modules/i18n/i18n';
|
||||
import * as notificationActions from './actions/notification';
|
||||
import * as authActions from './actions/auth';
|
||||
|
||||
export {
|
||||
Notification,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import timeago from 'timeago.js';
|
||||
import esTA from 'timeago.js/locales/es';
|
||||
import esTA from '../../../../node_modules/timeago.js/locales/es';
|
||||
|
||||
/**
|
||||
* Default locales, this should be overriden by config file
|
||||
@@ -0,0 +1,76 @@
|
||||
import {Map} from 'immutable';
|
||||
import * as actions from '../constants/auth';
|
||||
|
||||
const initialState = Map({
|
||||
isLoading: false,
|
||||
loggedIn: false,
|
||||
user: null,
|
||||
showSignInDialog: false,
|
||||
view: 'SIGNIN',
|
||||
error: '',
|
||||
successSignUp: false
|
||||
});
|
||||
|
||||
export default function auth (state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.SHOW_SIGNIN_DIALOG :
|
||||
return state
|
||||
.set('showSignInDialog', true);
|
||||
case actions.HIDE_SIGNIN_DIALOG :
|
||||
return state.merge(Map({
|
||||
isLoading: false,
|
||||
showSignInDialog: false,
|
||||
view: 'SIGNIN',
|
||||
error: '',
|
||||
successSignUp: false
|
||||
}));
|
||||
case actions.CHANGE_VIEW :
|
||||
return state
|
||||
.set('error', '')
|
||||
.set('view', action.view);
|
||||
case actions.CLEAN_STATE:
|
||||
return initialState;
|
||||
case actions.FETCH_SIGNIN_REQUEST:
|
||||
return state
|
||||
.set('isLoading', true);
|
||||
case actions.FETCH_SIGNIN_SUCCESS:
|
||||
return state
|
||||
.set('loggedIn', true)
|
||||
.set('user', action.user);
|
||||
case actions.FETCH_SIGNIN_FAILURE:
|
||||
return state
|
||||
.set('isLoading', false)
|
||||
.set('error', action.error);
|
||||
case actions.FETCH_SIGNIN_FACEBOOK_SUCCESS:
|
||||
return state
|
||||
.set('user', action.user)
|
||||
.set('loggedIn', true);
|
||||
case actions.FETCH_SIGNIN_FACEBOOK_FAILURE:
|
||||
return state
|
||||
.set('error', action.error)
|
||||
.set('user', null);
|
||||
case actions.FETCH_SIGNUP_REQUEST:
|
||||
return state
|
||||
.set('isLoading', true);
|
||||
case actions.FETCH_SIGNUP_FAILURE:
|
||||
return state
|
||||
.set('error', action.error)
|
||||
.set('isLoading', false);
|
||||
case actions.FETCH_SIGNUP_SUCCESS:
|
||||
return state
|
||||
.set('isLoading', false)
|
||||
.set('successSignUp', true);
|
||||
case actions.LOGOUT_SUCCESS:
|
||||
return state
|
||||
.set('loggedIn', false)
|
||||
.set('user', null);
|
||||
case actions.INVALID_FORM:
|
||||
return state
|
||||
.set('error', action.error);
|
||||
case actions.VALID_FORM:
|
||||
return state
|
||||
.set('error', '');
|
||||
default :
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {createStore, applyMiddleware} from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import mainReducer from './reducers';
|
||||
@@ -1,17 +0,0 @@
|
||||
/* Auth Actions */
|
||||
|
||||
export const SET_LOGGED_IN_USER = 'SET_LOGGED_IN_USER';
|
||||
export const LOG_OUT_USER = 'LOG_OUT_USER';
|
||||
|
||||
export const setLoggedInUser = (user_id) => {
|
||||
return {
|
||||
type: SET_LOGGED_IN_USER,
|
||||
user_id
|
||||
};
|
||||
};
|
||||
|
||||
export const LogOutUser = () => {
|
||||
return {
|
||||
type: LOG_OUT_USER
|
||||
};
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
/* Auth */
|
||||
|
||||
import * as actions from '../actions/auth';
|
||||
import {fromJS} from 'immutable';
|
||||
|
||||
const initialState = fromJS({});
|
||||
|
||||
export default (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case actions.SET_LOGGED_IN_USER:
|
||||
return state.set('user', action.user_id);
|
||||
case actions.LOG_OUT_USER:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"en": {
|
||||
"error": {
|
||||
"email": "Not a valid E-Mail",
|
||||
"password": "Password must be at least 8 characters",
|
||||
"displayName": "Display name is too short",
|
||||
"confirmPassword": "Passwords don`t match. Please, check again",
|
||||
"emailPasswordError": "Email and/or password combination incorrect.",
|
||||
"emailInUse": "Email address already in use"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"error": {
|
||||
"email": "No es un email válido",
|
||||
"password": "La contraseña debe tener por lo menos 8 caracteres",
|
||||
"displayName": "El nombre es muy corto",
|
||||
"confirmPassword": "Las contraseñas no coinciden",
|
||||
"emailPasswordError": "Email y/o contraseña incorrecta.",
|
||||
"emailInUse": "Email address already in use"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ class CommentBox extends Component {
|
||||
updateItem: PropTypes.func,
|
||||
id: PropTypes.string,
|
||||
comments: PropTypes.array,
|
||||
reply: PropTypes.bool
|
||||
reply: PropTypes.bool,
|
||||
canPost: PropTypes.bool
|
||||
}
|
||||
|
||||
state = {
|
||||
@@ -51,18 +52,9 @@ class CommentBox extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {styles, reply} = this.props;
|
||||
const {styles, reply, canPost} = this.props;
|
||||
// How to handle language in plugins? Should we have a dependency on our central translation file?
|
||||
return <div>
|
||||
<div className={`${name}-container`}>
|
||||
<input type='text'
|
||||
className={`${name}-username`}
|
||||
style={styles && styles.textarea}
|
||||
value={this.state.username}
|
||||
id={reply ? 'replyUser' : 'commentUser'}
|
||||
placeholder={lang.t('name')}
|
||||
onChange={(e) => this.setState({username: e.target.value})}/>
|
||||
</div>
|
||||
<div
|
||||
className={`${name}-container`}>
|
||||
<label
|
||||
@@ -81,12 +73,15 @@ class CommentBox extends Component {
|
||||
rows={3}/>
|
||||
</div>
|
||||
<div className={`${name}-button-container`}>
|
||||
<button
|
||||
className={`${name}-button`}
|
||||
style={styles && styles.button}
|
||||
onClick={this.postComment}>
|
||||
{lang.t('post')}
|
||||
</button>
|
||||
{ canPost && (
|
||||
<button
|
||||
className={`${name}-button`}
|
||||
style={styles && styles.button}
|
||||
onClick={this.postComment}>
|
||||
{lang.t('post')}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from './translations';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
const name = 'coral-plugin-permalinks';
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
|
||||
const Alert = ({cStyle = 'error', children, className, ...props}) => (
|
||||
<div
|
||||
className={`${styles.alert} ${styles[`alert--${cStyle}`]} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Alert;
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const ForgotContent = ({changeView, ...props}) => (
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h1>{lang.t('signIn.recoverPassword')}</h1>
|
||||
</div>
|
||||
<form onSubmit={(e) => {e.preventDefault(); props.fetchForgotPassword();}}>
|
||||
<div className={styles.formField}>
|
||||
<label htmlFor="email">{lang.t('signIn.email')}</label>
|
||||
<input type="text" id="email" />
|
||||
</div>
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
{lang.t('signIn.recoverPassword')}
|
||||
</Button>
|
||||
</form>
|
||||
<div className={styles.footer}>
|
||||
<span>{lang.t('signIn.needAnAccount')} <a onClick={() => changeView('SIGNUP')}>{lang.t('signIn.register')}</a></span>
|
||||
<span>{lang.t('signIn.alreadyHaveAnAccount')} <a onClick={() => changeView('SIGNIN')}>{lang.t('signIn.signIn')}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ForgotContent;
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
|
||||
const FormField = ({className, showErrors = false, errorMsg, label, ...props}) => (
|
||||
<div className={`${styles.formField} ${className ? className : ''}`}>
|
||||
<label htmlFor={props.id}>
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
className={showErrors && errorMsg ? styles.error : ''}
|
||||
name={props.id}
|
||||
{...props}
|
||||
/>
|
||||
{showErrors && errorMsg && <span className={styles.errorMsg}><span className={styles.attention}>!</span>{errorMsg}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default FormField;
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import {Dialog} from 'coral-ui';
|
||||
import styles from './styles.css';
|
||||
|
||||
import SignInContent from './SignInContent';
|
||||
import SingUpContent from './SignUpContent';
|
||||
import ForgotContent from './ForgotContent';
|
||||
|
||||
const SignDialog = ({open, view, handleClose, ...props}) => (
|
||||
<Dialog className={styles.dialog} open={open}>
|
||||
<span className={styles.close} onClick={handleClose}>×</span>
|
||||
{view === 'SIGNIN' && <SignInContent {...props} />}
|
||||
{view === 'SIGNUP' && <SingUpContent {...props} />}
|
||||
{view === 'FORGOT' && <ForgotContent {...props} />}
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
export default SignDialog;
|
||||
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import FormField from './FormField';
|
||||
import Alert from './Alert';
|
||||
import Spinner from 'coral-ui/components/Spinner';
|
||||
import styles from './styles.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const SignInContent = ({handleChange, formData, ...props}) => (
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h1>
|
||||
{lang.t('signIn.signIn')}
|
||||
</h1>
|
||||
</div>
|
||||
<div className={styles.socialConnections}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook}>
|
||||
{lang.t('signIn.facebookSignIn')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h1>
|
||||
{lang.t('signIn.or')}
|
||||
</h1>
|
||||
</div>
|
||||
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
|
||||
<form onSubmit={props.handleSignIn}>
|
||||
<FormField
|
||||
id="email"
|
||||
type="email"
|
||||
label={lang.t('signIn.email')}
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormField
|
||||
id="password"
|
||||
type="password"
|
||||
label={lang.t('signIn.password')}
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
{
|
||||
!props.auth.isLoading ?
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
{lang.t('signIn.signIn')}
|
||||
</Button>
|
||||
:
|
||||
<Spinner />
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
<div className={styles.footer}>
|
||||
<span><a onClick={() => props.changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')}</a></span>
|
||||
<span>
|
||||
{lang.t('signIn.needAnAccount')}
|
||||
<a onClick={() => props.changeView('SIGNUP')}>
|
||||
{lang.t('signIn.register')}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SignInContent;
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react';
|
||||
import FormField from './FormField';
|
||||
import Alert from './Alert';
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import Spinner from 'coral-ui/components/Spinner';
|
||||
import Success from 'coral-ui/components/Success';
|
||||
import styles from './styles.css';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
const SignUpContent = ({handleChange, formData, ...props}) => (
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h1>
|
||||
{lang.t('signIn.signUp')}
|
||||
</h1>
|
||||
</div>
|
||||
<div className={styles.socialConnections}>
|
||||
<Button cStyle="facebook" onClick={props.fetchSignInFacebook}>
|
||||
{lang.t('signIn.facebookSignUp')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h1>
|
||||
{lang.t('signIn.or')}
|
||||
</h1>
|
||||
</div>
|
||||
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
|
||||
<form onSubmit={props.handleSignUp}>
|
||||
<FormField
|
||||
id="email"
|
||||
type="email"
|
||||
label={lang.t('signIn.email')}
|
||||
value={formData.email}
|
||||
showErrors={props.showErrors}
|
||||
errorMsg={props.errors.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormField
|
||||
id="displayName"
|
||||
type="text"
|
||||
label={lang.t('signIn.displayName')}
|
||||
value={formData.displayName}
|
||||
showErrors={props.showErrors}
|
||||
errorMsg={props.errors.displayName}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormField
|
||||
id="password"
|
||||
type="password"
|
||||
label={lang.t('signIn.password')}
|
||||
value={formData.password}
|
||||
showErrors={props.showErrors}
|
||||
errorMsg={props.errors.password}
|
||||
onChange={handleChange}
|
||||
minLength="8"
|
||||
/>
|
||||
{ !props.errors.password && <span className={styles.hint}> Password must be at least 8 characters. </span> }
|
||||
<FormField
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
label={lang.t('signIn.confirmPassword')}
|
||||
value={formData.confirmPassword}
|
||||
showErrors={props.showErrors}
|
||||
errorMsg={props.errors.confirmPassword}
|
||||
onChange={handleChange}
|
||||
minLength="8"
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
{ !props.auth.isLoading && !props.auth.successSignUp && (
|
||||
<Button type="submit" cStyle="black" className={styles.signInButton}>
|
||||
{lang.t('signIn.signUp')}
|
||||
</Button>
|
||||
)}
|
||||
{ props.auth.isLoading && <Spinner /> }
|
||||
{ !props.auth.isLoading && props.auth.successSignUp && <Success /> }
|
||||
</div>
|
||||
</form>
|
||||
<div className={styles.footer}>
|
||||
<span>
|
||||
{lang.t('signIn.alreadyHaveAnAccount')}
|
||||
<a onClick={() => props.changeView('SIGNIN')}>
|
||||
{lang.t('signIn.signIn')}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SignUpContent;
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
|
||||
const UserBox = ({className, user, logout, ...props}) => (
|
||||
<div
|
||||
className={`${styles.userBox} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
Signed in as <a>{user.displayName}</a>. Not you? <a onClick={logout}>Sign out</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default UserBox;
|
||||
@@ -0,0 +1,131 @@
|
||||
.dialog {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 280px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1, .separator h1{
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.formField {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.formField label {
|
||||
font-size: 1.08em;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.formField input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
border: none;
|
||||
outline: none;
|
||||
border: 1px solid rgba(0,0,0,.12);
|
||||
padding: 10px 6px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 2px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 20px auto 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer span {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.socialConnections {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.signInButton {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 20px;
|
||||
line-height: 14px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
color: #6b6b6b;
|
||||
}
|
||||
|
||||
input.error{
|
||||
border: solid 2px #f44336;
|
||||
}
|
||||
|
||||
.errorMsg, .hint {
|
||||
color: grey;
|
||||
font-weight: 600;
|
||||
padding: 3px 0 16px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.alert--success {
|
||||
border: solid 1px #1ec00e;
|
||||
background: #cbf1b8;
|
||||
color: #006900;
|
||||
}
|
||||
|
||||
.alert--error {
|
||||
background: #FFEBEE;
|
||||
color: #B71C1C;
|
||||
}
|
||||
|
||||
.userBox a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.attention {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: #B71C1C;
|
||||
color: #FFEBEE;
|
||||
font-weight: bolder;
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
font-size: 9px;
|
||||
line-height: 7px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 15px;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import SignDialog from '../components/SignDialog';
|
||||
import Button from 'coral-ui/components/Button';
|
||||
import validate from 'coral-framework/helpers/validate';
|
||||
import errorMsj from 'coral-framework/helpers/error';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
import translations from '../translations';
|
||||
const lang = new I18n(translations);
|
||||
|
||||
import {
|
||||
changeView,
|
||||
fetchSignUp,
|
||||
fetchSignIn,
|
||||
showSignInDialog,
|
||||
hideSignInDialog,
|
||||
fetchSignInFacebook,
|
||||
fetchForgotPassword,
|
||||
facebookCallback,
|
||||
invalidForm,
|
||||
validForm
|
||||
} from '../../coral-framework/actions/auth';
|
||||
|
||||
class SignInContainer extends Component {
|
||||
initialState = {
|
||||
formData: {
|
||||
email: '',
|
||||
displayName: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
},
|
||||
errors: {},
|
||||
showErrors: false
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = this.initialState;
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSignUp = this.handleSignUp.bind(this);
|
||||
this.handleSignIn = this.handleSignIn.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
this.addError = this.addError.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.authCallback = this.props.facebookCallback;
|
||||
const {formData} = this.state;
|
||||
const errors = Object.keys(formData).reduce((map, prop) => {
|
||||
map[prop] = lang.t('signIn.requiredField');
|
||||
return map;
|
||||
}, {});
|
||||
this.setState({errors});
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const {name, value} = e.target;
|
||||
this.setState(state => ({
|
||||
...state,
|
||||
formData: {
|
||||
...state.formData,
|
||||
[name]: value
|
||||
}
|
||||
}), () => {
|
||||
this.validation(name, value);
|
||||
});
|
||||
}
|
||||
|
||||
addError(name, error) {
|
||||
return this.setState(state => ({
|
||||
errors: {
|
||||
...state.errors,
|
||||
[name]: error
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
validation(name, value) {
|
||||
const {addError} = this;
|
||||
const {formData} = this.state;
|
||||
|
||||
if (!value.length) {
|
||||
addError(name, lang.t('signIn.requiredField'));
|
||||
} else if (name === 'confirmPassword' && formData.confirmPassword !== formData.password) {
|
||||
addError('confirmPassword', lang.t('signIn.passwordsDontMatch'));
|
||||
} else if (!validate[name](value)) {
|
||||
addError(name, errorMsj[name]);
|
||||
} else {
|
||||
const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line
|
||||
// Removes Error
|
||||
this.setState(state => ({...state, errors}));
|
||||
}
|
||||
}
|
||||
|
||||
isCompleted() {
|
||||
const {formData} = this.state;
|
||||
return !Object.keys(formData).filter(prop => !formData[prop].length).length;
|
||||
}
|
||||
|
||||
displayErrors(show = true) {
|
||||
this.setState({showErrors: show});
|
||||
}
|
||||
|
||||
handleSignUp(e) {
|
||||
e.preventDefault();
|
||||
const {errors} = this.state;
|
||||
const {fetchSignUp, validForm, invalidForm} = this.props;
|
||||
this.displayErrors();
|
||||
if (this.isCompleted() && !Object.keys(errors).length) {
|
||||
fetchSignUp(this.state.formData);
|
||||
validForm();
|
||||
} else {
|
||||
invalidForm(lang.t('signIn.checkTheForm'));
|
||||
}
|
||||
}
|
||||
|
||||
handleSignIn(e) {
|
||||
e.preventDefault();
|
||||
this.props.fetchSignIn(this.state.formData);
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
this.props.hideSignInDialog();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {auth, showSignInDialog} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={showSignInDialog}>
|
||||
Sign in to comment
|
||||
</Button>
|
||||
<SignDialog
|
||||
open={auth.showSignInDialog}
|
||||
view={auth.view}
|
||||
{...this}
|
||||
{...this.state}
|
||||
{...this.props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth.toJS()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
facebookCallback: (err, data) => dispatch(facebookCallback(err, data)),
|
||||
fetchSignUp: formData => dispatch(fetchSignUp(formData)),
|
||||
fetchSignIn: formData => dispatch(fetchSignIn(formData)),
|
||||
fetchSignInFacebook: () => dispatch(fetchSignInFacebook()),
|
||||
fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)),
|
||||
showSignInDialog: () => dispatch(showSignInDialog()),
|
||||
changeView: view => dispatch(changeView(view)),
|
||||
handleClose: () => dispatch(hideSignInDialog()),
|
||||
invalidForm: error => dispatch(invalidForm(error)),
|
||||
validForm: () => dispatch(validForm())
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SignInContainer);
|
||||
@@ -0,0 +1,48 @@
|
||||
export default {
|
||||
en: {
|
||||
'signIn': {
|
||||
facebookSignIn: 'Sign in with Facebook',
|
||||
facebookSignUp: 'Sign up with Facebook',
|
||||
logout: 'Logout',
|
||||
signIn: 'Sign In',
|
||||
or: 'Or',
|
||||
email: 'E-mail Address',
|
||||
password: 'Password',
|
||||
forgotYourPass: 'Forgot your password?',
|
||||
needAnAccount: 'Need an account?',
|
||||
register: 'Register',
|
||||
signUp: 'Sign Up',
|
||||
confirmPassword: 'Confirm Password',
|
||||
displayName: 'Display Name',
|
||||
alreadyHaveAnAccount: 'Already have an account?',
|
||||
recoverPassword: 'Recover password',
|
||||
emailInUse: 'Email address already in use',
|
||||
requiredField: 'This field is required',
|
||||
passwordsDontMatch: 'Passwords don\'t match.',
|
||||
checkTheForm: 'Invalid Form. Please, check the fields'
|
||||
}
|
||||
},
|
||||
es: {
|
||||
'signIn': {
|
||||
facebookSignIn: 'Entrar con Facebook',
|
||||
facebookSignUp: 'Regístrate con Facebook',
|
||||
logout: 'Salir',
|
||||
signIn: 'Entrar',
|
||||
or: 'o',
|
||||
email: 'E-mail',
|
||||
password: 'Contraseña',
|
||||
forgotYourPass: 'Has olvidado tu contraseña?',
|
||||
needAnAccount: 'Necesitas una cuenta?',
|
||||
register: 'Regístrate',
|
||||
signUp: 'Registro',
|
||||
confirmPassword: 'Confirmar Contraseña',
|
||||
displayName: 'Nombre',
|
||||
alreadyHaveAnAccount: 'Ya tienes una cuenta?',
|
||||
recoverPassword: 'Recuperar contraseña',
|
||||
emailInUse: 'Este email se encuentra en uso',
|
||||
requiredField: 'Este campo es requerido',
|
||||
passwordsDontMatch: 'Las contraseñas no coinciden',
|
||||
checkTheForm: 'Formulario Inválido. Por favor, completa los campos'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
.button {
|
||||
background: 0 0;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
color: #000;
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 36px;
|
||||
min-width: 64px;
|
||||
padding: 0 8px;
|
||||
display: inline-block;
|
||||
font-family: 'Roboto','Helvetica','Arial',sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
overflow: hidden;
|
||||
will-change: box-shadow,transform;
|
||||
-webkit-transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
|
||||
transition: box-shadow .2s cubic-bezier(.4,0,1,1),background-color .2s cubic-bezier(.4,0,.2,1),color .2s cubic-bezier(.4,0,.2,1);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.type--black {
|
||||
color: #E0E0E0;
|
||||
background: #212121;
|
||||
}
|
||||
|
||||
.type--local {
|
||||
background: #E0E0E0;
|
||||
color: #212121;
|
||||
}
|
||||
|
||||
.type--facebook {
|
||||
background-color: #4267b2;
|
||||
border-color: #4267b2;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.type--facebook:hover {
|
||||
background-color: #365899;
|
||||
border-color: #365899;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './Button.css';
|
||||
|
||||
const Button = ({cStyle = 'local', children, className, ...props}) => (
|
||||
<button
|
||||
className={`${styles.button} ${styles[`type--${cStyle}`]} ${className}`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default Button;
|
||||
@@ -0,0 +1,62 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import dialogPolyfill from 'dialog-polyfill';
|
||||
import 'dialog-polyfill/dialog-polyfill.css';
|
||||
|
||||
export default class Dialog extends Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onCancel: PropTypes.func,
|
||||
onClose: PropTypes.func,
|
||||
open: PropTypes.bool,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onCancel: e => e.preventDefault(),
|
||||
onClose: e => e.preventDefault()
|
||||
};
|
||||
|
||||
componentDidMount(){
|
||||
const dialog = ReactDOM.findDOMNode(this.refs.dialog);
|
||||
dialogPolyfill.registerDialog(dialog);
|
||||
|
||||
if (this.props.open) {
|
||||
dialog.showModal();
|
||||
}
|
||||
|
||||
dialog.addEventListener('close', this.props.onClose);
|
||||
dialog.addEventListener('cancel', this.props.onCancel);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const dialog = ReactDOM.findDOMNode(this.refs.dialog);
|
||||
if (this.props.open !== prevProps.open) {
|
||||
if (this.props.open) {
|
||||
dialog.showModal();
|
||||
} else {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const dialog = ReactDOM.findDOMNode(this.refs.dialog);
|
||||
dialog.removeEventListener('cancel', this.props.onCancel);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {children, className = '', onClose, onCancel, open, ...rest} = this.props; // eslint-disable-line
|
||||
|
||||
return (
|
||||
<dialog
|
||||
ref="dialog"
|
||||
className={`mdl-dialog ${className}`}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
.container {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
-webkit-animation: rotator 1.4s linear infinite;
|
||||
animation: rotator 1.4s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotator {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotator {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
}
|
||||
.path {
|
||||
stroke-dasharray: 187;
|
||||
stroke-dashoffset: 0;
|
||||
-webkit-transform-origin: center;
|
||||
transform-origin: center;
|
||||
-webkit-animation: dash 1.4s ease-in-out infinite, colors 5.6s ease-in-out infinite;
|
||||
animation: dash 1.4s ease-in-out infinite, colors 5.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes colors {
|
||||
0% {
|
||||
stroke: #f67150;
|
||||
}
|
||||
100% {
|
||||
stroke: #f6a47e;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colors {
|
||||
0% {
|
||||
stroke: #f67150;
|
||||
}
|
||||
100% {
|
||||
stroke: #f6a47e;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes dash {
|
||||
0% {
|
||||
stroke-dashoffset: 187;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 46.75;
|
||||
-webkit-transform: rotate(135deg);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 187;
|
||||
-webkit-transform: rotate(450deg);
|
||||
transform: rotate(450deg);
|
||||
}
|
||||
}
|
||||
@keyframes dash {
|
||||
0% {
|
||||
stroke-dashoffset: 187;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 46.75;
|
||||
-webkit-transform: rotate(135deg);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 187;
|
||||
-webkit-transform: rotate(450deg);
|
||||
transform: rotate(450deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import styles from './Spinner.css';
|
||||
|
||||
const Spinner = () => (
|
||||
<div className={styles.container}>
|
||||
<svg className={styles.spinner} width="40px" height="40px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle className={styles.path} fill="none" strokeWidth="6" strokeLinecap="round" cx="33" cy="33" r="30"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Spinner;
|
||||
@@ -0,0 +1,55 @@
|
||||
.container {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.circle {
|
||||
stroke-dasharray: 166;
|
||||
stroke-dashoffset: 166;
|
||||
stroke-width: 2;
|
||||
stroke-miterlimit: 10;
|
||||
stroke: #00897B;
|
||||
fill: none;
|
||||
animation: stroke .6s cubic-bezier(0.650, 0.000, 0.450, 1.000) forwards;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
stroke-width: 2;
|
||||
stroke: #fff;
|
||||
stroke-miterlimit: 10;
|
||||
margin: 10% auto;
|
||||
box-shadow: inset 0px 0px 0px #00897B;
|
||||
animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
|
||||
}
|
||||
|
||||
.check {
|
||||
transform-origin: 50% 50%;
|
||||
stroke-dasharray: 48;
|
||||
stroke-dashoffset: 48;
|
||||
animation: stroke .3s cubic-bezier(0.650, 0.000, 0.450, 1.000) .8s forwards;
|
||||
}
|
||||
|
||||
@keyframes stroke {
|
||||
100% {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale {
|
||||
0%, 100% {
|
||||
transform: none;
|
||||
}
|
||||
50% {
|
||||
transform: scale3d(1.1, 1.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fill {
|
||||
100% {
|
||||
box-shadow: inset 0px 0px 0px 30px #00897B;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import styles from './Success.css';
|
||||
|
||||
const Success = () => (
|
||||
<div className={styles.container}>
|
||||
<svg className={styles.checkmark} viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle className={styles.circle} cx="26" cy="26" r="25" fill="none"></circle>
|
||||
<path className={styles.check} fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Success;
|
||||
@@ -0,0 +1 @@
|
||||
export {default as Dialog} from './components/Dialog';
|
||||
Reference in New Issue
Block a user