Merge pull request #510 from coralproject/login-popup

Login Popup
This commit is contained in:
David Erwin
2017-04-20 16:10:18 -04:00
committed by GitHub
19 changed files with 112 additions and 86 deletions
@@ -0,0 +1,16 @@
import React from 'react';
import {Router, Route, browserHistory} from 'react-router';
import Embed from './Embed';
import SignInContainer from 'coral-sign-in/containers/SignInContainer';
const routes = (
<div>
<Route exact path="/embed/stream/login" component={SignInContainer}/>
<Route exact path="*" component={Embed}/>
</div>
);
const AppRouter = () => <Router history={browserHistory} routes={routes} />;
export default AppRouter;
+1 -1
View File
@@ -19,7 +19,7 @@ import FlagComment from 'coral-plugin-flags/FlagComment';
import LikeButton from 'coral-plugin-likes/LikeButton';
import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest, BestIndicator} from 'coral-plugin-best/BestButton';
import LoadMore from 'coral-embed-stream/src/LoadMore';
import {Slot} from 'coral-framework';
import Slot from 'coral-framework/components/Slot';
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import {TopRightMenu} from './TopRightMenu';
import {getActionSummary, getTotalActionCount, iPerformedThisAction} from 'coral-framework/utils';
+11 -12
View File
@@ -8,7 +8,7 @@ const lang = new I18n(translations);
import {TabBar, Tab, TabContent, Spinner, Button} from 'coral-ui';
const {logout, showSignInDialog, requestConfirmEmail} = authActions;
const {logout, showSignInDialog, requestConfirmEmail, openSignInPopUp, checkLogin} = authActions;
const {addNotification, clearNotification} = notificationActions;
const {fetchAssetSuccess} = assetActions;
import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments';
@@ -26,7 +26,6 @@ import {ModerationLink} from 'coral-plugin-moderation';
import Count from 'coral-plugin-comment-count/CommentCount';
import CommentBox from 'coral-plugin-commentbox/CommentBox';
import UserBox from 'coral-sign-in/components/UserBox';
import SignInContainer from 'coral-sign-in/containers/SignInContainer';
import SuspendedAccount from 'coral-framework/components/SuspendedAccount';
import ChangeUsernameContainer from '../../coral-sign-in/containers/ChangeUsernameContainer';
import ProfileContainer from 'coral-settings/containers/ProfileContainer';
@@ -78,6 +77,7 @@ class Embed extends React.Component {
componentDidMount () {
pym.sendMessage('childReady');
this.props.checkLogin();
}
componentWillUnmount () {
@@ -119,8 +119,7 @@ class Embed extends React.Component {
setActiveReplyBox = (reactKey) => {
if (!this.props.auth.user) {
const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75;
this.props.showSignInDialog(offset);
this.props.showSignInDialog();
} else {
this.setState({activeReplyBox: reactKey});
}
@@ -130,7 +129,7 @@ class Embed extends React.Component {
const {activeTab} = this.state;
const {closedAt, countCache = {}} = this.props.asset;
const {asset, refetch, comment} = this.props.data;
const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth;
const {loggedIn, isAdmin, user, showSignInDialog} = this.props.auth;
// even though the permalinked comment is the highlighted one, we're displaying its parent + replies
const highlightedComment = comment && comment.parent ? comment.parent : comment;
@@ -218,11 +217,10 @@ class Embed extends React.Component {
</div>
: <p>{asset.settings.closedMessage}</p>
}
{!loggedIn && <SignInContainer
requireEmailConfirmation={asset.settings.requireEmailConfirmation}
refetch={refetch}
offset={signInOffset}/>}
{loggedIn && user && <ChangeUsernameContainer loggedIn={loggedIn} offset={signInOffset} user={user} />}
{!loggedIn && <Button id='coralSignInButton' onClick={this.props.showSignInDialog} full>Sign in to comment</Button>}
{loggedIn && user && <ChangeUsernameContainer loggedIn={loggedIn} user={user} />}
{loggedIn && <ModerationLink assetId={asset.id} isAdmin={isAdmin} />}
{/* the highlightedComment is isolated after the user followed a permalink */}
@@ -290,7 +288,6 @@ class Embed extends React.Component {
<ProfileContainer
loggedIn={loggedIn}
userData={this.props.userData}
showSignInDialog={this.props.showSignInDialog}
/>
</TabContent>
<TabContent show={activeTab === 2}>
@@ -320,10 +317,12 @@ const mapDispatchToProps = dispatch => ({
addNotification: (type, text) => addNotification(type, text),
clearNotification: () => dispatch(clearNotification()),
editName: (username) => dispatch(editName(username)),
showSignInDialog: (offset) => dispatch(showSignInDialog(offset)),
showSignInDialog: () => dispatch(showSignInDialog()),
updateCountCache: (id, count) => dispatch(updateCountCache(id, count)),
viewAllComments: () => dispatch(viewAllComments()),
logout: () => dispatch(logout()),
openSignInPopUp: cb => dispatch(openSignInPopUp(cb)),
checkLogin: () => dispatch(checkLogin()),
dispatch: d => dispatch(d),
});
+5 -3
View File
@@ -3,13 +3,15 @@ import {render} from 'react-dom';
import {ApolloProvider} from 'react-apollo';
import {client} from 'coral-framework/services/client';
import store from 'coral-framework/services/store';
import localStore from 'coral-framework/services/store';
import Embed from './Embed';
import AppRouter from './AppRouter';
const store = (window.opener && window.opener.coralStore) ? window.opener.coralStore : localStore;
render(
<ApolloProvider client={client} store={store}>
<Embed />
<AppRouter />
</ApolloProvider>
, document.querySelector('#coralStream')
);
+46 -11
View File
@@ -11,6 +11,16 @@ const ME_QUERY = gql`
query Me {
me {
status
comments {
id
body
asset {
id
title
url
}
created_at
}
}
}
`;
@@ -22,8 +32,23 @@ function fetchMe() {
}
// Dialog Actions
export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset});
export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG});
export const showSignInDialog = () => dispatch => {
const signInPopUp = window.open(
'/embed/stream/login',
'Login',
'menubar=0,resizable=0,width=500,height=550,top=200,left=500'
);
signInPopUp.onbeforeunload = () => {
dispatch(checkLogin());
fetchMe();
};
dispatch({type: actions.SHOW_SIGNIN_DIALOG});
};
export const hideSignInDialog = () => dispatch => {
dispatch({type: actions.HIDE_SIGNIN_DIALOG});
window.close();
};
export const createUsernameRequest = () => ({type: actions.CREATE_USERNAME_REQUEST});
export const showCreateUsernameDialog = () => ({type: actions.SHOW_CREATEUSERNAME_DIALOG});
@@ -47,29 +72,39 @@ export const createUsername = (userId, formData) => dispatch => {
});
};
export const changeView = view => dispatch =>
export const changeView = view => dispatch => {
dispatch({
type: actions.CHANGE_VIEW,
view
});
switch(view) {
case 'SIGNUP':
window.resizeTo(500, 800);
break;
case 'FORGOT':
window.resizeTo(500, 400);
break;
default:
window.resizeTo(500, 550);
}
};
export const cleanState = () => ({type: actions.CLEAN_STATE});
// Sign In Actions
const signInRequest = () => ({type: actions.FETCH_SIGNIN_REQUEST});
const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin});
// TODO: revisit login redux flow.
// const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin});
//
const signInFailure = error => ({type: actions.FETCH_SIGNIN_FAILURE, error});
export const fetchSignIn = (formData) => (dispatch) => {
dispatch(signInRequest());
return coralApi('/auth/local', {method: 'POST', body: formData})
.then(({user}) => {
const isAdmin = !!user && !!user.roles.filter(i => i === 'ADMIN').length;
dispatch(signInSuccess(user, isAdmin));
dispatch(hideSignInDialog());
fetchMe();
})
.then(() => dispatch(hideSignInDialog()))
.catch(error => {
if (error.metadata) {
@@ -121,7 +156,7 @@ export const facebookCallback = (err, data) => dispatch => {
dispatch(signInFacebookSuccess(user));
dispatch(hideSignInDialog());
dispatch(showCreateUsernameDialog());
fetchMe();
dispatch(hideSignInDialog());
} catch (err) {
dispatch(signInFacebookFailure(err));
return;
-4
View File
@@ -1,16 +1,12 @@
import store from './services/store';
import pym from './services/PymConnection';
import I18n from './modules/i18n/i18n';
import actions from './actions';
import Slot from './components/Slot';
// TODO (bc): Deprecate old actions. Spreading actions is now needed.
export default {
pym,
Slot,
I18n,
store,
actions,
...actions
};
+1 -2
View File
@@ -28,8 +28,7 @@ export default function auth (state = initialState, action) {
switch (action.type) {
case actions.SHOW_SIGNIN_DIALOG :
return state
.set('showSignInDialog', true)
.set('signInOffset', action.offset);
.set('showSignInDialog', true);
case actions.HIDE_SIGNIN_DIALOG :
return state.merge(Map({
isLoading: false,
+4 -1
View File
@@ -24,7 +24,7 @@ if (window.devToolsExtension) {
middlewares.push(window.devToolsExtension());
}
export default createStore(
const store = createStore(
combineReducers({
...mainReducer,
apollo: client.reducer()
@@ -32,3 +32,6 @@ export default createStore(
{},
compose(...middlewares)
);
export default store;
window.coralStore = store;
+1 -1
View File
@@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react';
import {I18n} from '../coral-framework';
import translations from './translations.json';
import {Button} from 'coral-ui';
import {Slot} from 'coral-framework';
import Slot from 'coral-framework/components/Slot';
import {connect} from 'react-redux';
const name = 'coral-plugin-commentbox';
+1 -2
View File
@@ -25,8 +25,7 @@ class FlagButton extends Component {
const {localPost, localDelete} = this.state;
const localFlagged = (flaggedByCurrentUser && !localDelete) || localPost;
if (!currentUser) {
const offset = document.getElementById(`c_${this.props.id}`).getBoundingClientRect().top - 75;
this.props.showSignInDialog(offset);
this.props.showSignInDialog();
return;
}
if (localFlagged) {
+1 -2
View File
@@ -35,8 +35,7 @@ class LikeButton extends Component {
const onLikeClick = () => {
if (!currentUser) {
const offset = document.getElementById(`c_${id}`).getBoundingClientRect().top - 75;
showSignInDialog(offset);
showSignInDialog();
return;
}
if (currentUser.banned) {
@@ -1,17 +1,13 @@
import React from 'react';
import styles from './NotLoggedIn.css';
import SignInContainer from '../../coral-sign-in/containers/SignInContainer';
import translations from '../translations';
import I18n from 'coral-framework/modules/i18n/i18n';
const lang = new I18n(translations);
export default ({showSignInDialog, requireEmailConfirmation}) => (
export default ({showSignInDialog}) => (
<div className={styles.message}>
<SignInContainer noButton={true} requireEmailConfirmation={requireEmailConfirmation}/>
<div>
<a onClick={() => {
showSignInDialog();
}}>{lang.t('signIn')}</a> {lang.t('toAccess')}
<a onClick={showSignInDialog}>{lang.t('signIn')}</a> {lang.t('toAccess')}
</div>
<div>
{lang.t('fromSettingsPage')}
@@ -2,6 +2,7 @@ import {connect} from 'react-redux';
import {compose} from 'react-apollo';
import React, {Component} from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
import {bindActionCreators} from 'redux';
import {myCommentHistory, myIgnoredUsers} from 'coral-framework/graphql/queries';
import {stopIgnoringUser} from 'coral-framework/graphql/mutations';
@@ -12,6 +13,8 @@ import IgnoredUsers from '../components/IgnoredUsers';
import {Spinner} from 'coral-ui';
import CommentHistory from 'coral-plugin-history/CommentHistory';
import {showSignInDialog, checkLogin} from 'coral-framework/actions/auth';
import translations from '../translations';
const lang = new I18n(translations);
@@ -32,17 +35,17 @@ class ProfileContainer extends Component {
}
render() {
const {loggedIn, asset, showSignInDialog, data, myIgnoredUsersData, stopIgnoringUser} = this.props;
const {loggedIn, asset, data, showSignInDialog, myIgnoredUsersData, stopIgnoringUser} = this.props;
const {me} = this.props.data;
if (!loggedIn || !me) {
return <NotLoggedIn showSignInDialog={showSignInDialog} requireEmailConfirmation={asset.settings.requireEmailConfirmation}/>;
}
if (data.loading) {
return <Spinner/>;
}
if (!loggedIn || !me) {
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
}
const localProfile = this.props.user.profiles.find(p => p.provider === 'local');
const emailAddress = localProfile && localProfile.id;
@@ -81,7 +84,6 @@ class ProfileContainer extends Component {
:
<p>{lang.t('userNoComment')}</p>
}
</div>
);
}
@@ -93,10 +95,8 @@ const mapStateToProps = state => ({
auth: state.auth.toJS()
});
const mapDispatchToProps = () => ({
// saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData))
});
const mapDispatchToProps = dispatch =>
bindActionCreators({showSignInDialog, checkLogin}, dispatch);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
@@ -10,16 +10,12 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
const CreateUsernameDialog = ({open, handleClose, offset, formData, handleSubmitUsername, handleChange, ...props}) => {
const CreateUsernameDialog = ({open, handleClose, formData, handleSubmitUsername, handleChange, ...props}) => {
return (
<Dialog
className={styles.dialogusername}
id="createUsernameDialog"
open={open}
style={{
position: 'relative',
top: offset !== 0 && offset
}}>
open={open}>
<span className={styles.close} onClick={handleClose}>×</span>
<div>
<div className={styles.header}>
@@ -6,15 +6,11 @@ import SignInContent from './SignInContent';
import SignUpContent from './SignUpContent';
import ForgotContent from './ForgotContent';
const SignDialog = ({open, view, handleClose, offset, ...props}) => (
const SignDialog = ({open, view, handleClose, ...props}) => (
<Dialog
className={styles.dialog}
id="signInDialog"
open={open}
style={{
position: 'fixed',
top: offset !== 0 && offset
}}>
open={open}>
<span className={styles.close} onClick={handleClose}>×</span>
{view === 'SIGNIN' && <SignInContent {...props} />}
{view === 'SIGNUP' && <SignUpContent {...props} />}
@@ -20,8 +20,8 @@ const SignInContent = ({
}) => {
return (
<div>
<div className={styles.header}>
<div className="coral-sign-in">
<div className={`${styles.header} header`}>
<h1>
{auth.emailVerificationFailure ? lang.t('signIn.emailVerifyCTA') : lang.t('signIn.signIn')}
</h1>
@@ -42,7 +42,7 @@ const SignInContent = ({
{emailVerificationSuccess && <Success />}
</form>
: <div>
<div className={styles.socialConnections}>
<div className={`${styles.socialConnections} social-connections`}>
<Button cStyle="facebook" onClick={fetchSignInFacebook} full>
{lang.t('signIn.facebookSignIn')}
</Button>
@@ -80,7 +80,7 @@ const SignInContent = ({
</form>
</div>
}
<div className={styles.footer}>
<div className={`${styles.footer} footer`}>
<span><a onClick={() => changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')}</a></span>
<span>
{lang.t('signIn.needAnAccount')}
@@ -100,12 +100,11 @@ class ChangeUsernameContainer extends Component {
}
render() {
const {loggedIn, auth, offset} = this.props;
const {loggedIn, auth} = this.props;
return (
<div>
<CreateUsernameDialog
open={auth.showCreateUsernameDialog && auth.user.canEditName}
offset={offset}
handleClose={this.handleClose}
loggedIn={loggedIn}
handleSubmitUsername={this.handleSubmitUsername}
@@ -1,7 +1,6 @@
import React, {Component, PropTypes} 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';
@@ -13,7 +12,6 @@ import {
changeView,
fetchSignUp,
fetchSignIn,
showSignInDialog,
hideSignInDialog,
fetchSignInFacebook,
fetchSignUpFacebook,
@@ -147,23 +145,18 @@ class SignInContainer extends Component {
handleSignIn(e) {
e.preventDefault();
this.props.fetchSignIn(this.state.formData)
.then(this.props.refetch);
this.props.fetchSignIn(this.state.formData);
}
render() {
const {auth, showSignInDialog, noButton, offset, requireEmailConfirmation} = this.props;
const {auth, requireEmailConfirmation} = this.props;
const {emailVerificationLoading, emailVerificationSuccess} = auth;
return (
<div>
{!noButton && <Button id='coralSignInButton' onClick={showSignInDialog} full>
Sign in to comment
</Button>}
<SignDialog
open={auth.showSignInDialog}
open={true}
view={auth.view}
offset={offset}
emailVerificationEnabled={requireEmailConfirmation}
emailVerificationLoading={emailVerificationLoading}
emailVerificationSuccess={emailVerificationSuccess}
@@ -189,7 +182,6 @@ const mapDispatchToProps = dispatch => ({
fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()),
fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)),
requestConfirmEmail: (email, url) => dispatch(requestConfirmEmail(email, url)),
showSignInDialog: () => dispatch(showSignInDialog()),
changeView: view => dispatch(changeView(view)),
handleClose: () => dispatch(hideSignInDialog()),
invalidForm: error => dispatch(invalidForm(error)),
@@ -19,8 +19,7 @@ class RespectButton extends Component {
// If the current user does not exist, trigger sign in dialog.
if (!me) {
const offset = document.getElementById(`c_${commentId}`).getBoundingClientRect().top - 75;
showSignInDialog(offset);
showSignInDialog();
return;
}