import jwtDecode from 'jwt-decode'; import bowser from 'bowser'; import * as actions from '../constants/auth'; import * as Storage from '../helpers/storage'; import coralApi, {base} from '../helpers/request'; import pym from '../services/pym'; import {resetWebsocket} from 'coral-framework/services/client'; import t from 'coral-framework/services/i18n'; import {isSlotEmpty} from 'plugin-api/beta/client/services'; export const showSignInDialog = () => (dispatch, getState) => { if (isSlotEmpty('login')) { return; } const signInPopUp = window.open( '/embed/stream/login', 'Login', 'menubar=0,resizable=0,width=500,height=550,top=200,left=500' ); // Workaround odd behavior in older WebKit versions, where // onunload is called twice. (Encountered in IOS 8.3) let loaded = false; signInPopUp.onload = () => { loaded = true; // Fire some actions inside the popups reducer, to initialize required state. const required = getState().asset.toJS().settings.requireEmailConfirmation; const redirectUri = getState().auth.toJS().redirectUri; signInPopUp.coralStore.dispatch(setRequireEmailVerification(required)); signInPopUp.coralStore.dispatch(setRedirectUri(redirectUri)); }; // Use `onunload` instead of `onbeforeunload` which is not supported in IOS Safari. signInPopUp.onunload = () => { if (loaded) { dispatch(checkLogin()); } }; 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 }); export const hideCreateUsernameDialog = () => ({ type: actions.HIDE_CREATEUSERNAME_DIALOG }); const createUsernameSuccess = () => ({ type: actions.CREATE_USERNAME_SUCCESS }); const createUsernameFailure = (error) => ({ type: actions.CREATE_USERNAME_FAILURE, error }); export const updateUsername = ({username}) => ({ type: actions.UPDATE_USERNAME, username }); export const createUsername = (userId, formData) => (dispatch) => { dispatch(createUsernameRequest()); coralApi('/account/username', {method: 'PUT', body: formData}) .then(() => { dispatch(createUsernameSuccess()); dispatch(hideCreateUsernameDialog()); dispatch(updateUsername(formData)); }) .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(createUsernameFailure(errorMessage)); }); }; 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 signInFailure = (error) => ({ type: actions.FETCH_SIGNIN_FAILURE, error }); //============================================================================== // AUTH TOKEN //============================================================================== export const handleAuthToken = (token) => (dispatch) => { Storage.setItem('exp', jwtDecode(token).exp); Storage.setItem('token', token); dispatch({type: 'HANDLE_AUTH_TOKEN'}); }; //============================================================================== // SIGN IN //============================================================================== export const fetchSignIn = (formData) => { return (dispatch) => { dispatch(signInRequest()); return coralApi('/auth/local', {method: 'POST', body: formData}) .then(({token}) => { if (!bowser.safari && !bowser.ios) { dispatch(handleAuthToken(token)); } dispatch(hideSignInDialog()); }) .catch((error) => { console.error(error); if (error.metadata) { // the user might not have a valid email. prompt the user user re-request the confirmation email dispatch( signInFailure(t('error.email_not_verified', error.metadata)) ); } else if (error.translation_key === 'NOT_AUTHORIZED') { // invalid credentials dispatch(signInFailure(t('error.email_password'), error.metadata)); } else { const str = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(signInFailure(str)); } }); }; }; //============================================================================== // 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' ); }; //============================================================================== // SIGN UP - FACEBOOK //============================================================================== const signUpFacebookRequest = () => ({ type: actions.FETCH_SIGNUP_FACEBOOK_REQUEST }); export const fetchSignUpFacebook = () => (dispatch) => { dispatch(signUpFacebookRequest()); 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) { dispatch(signInFacebookFailure(err)); return; } try { dispatch(handleAuthToken(data.token)); dispatch(signInFacebookSuccess(data.user)); dispatch(hideSignInDialog()); } catch (err) { dispatch(signInFacebookFailure(err)); return; } }; //============================================================================== // SIGN UP //============================================================================== 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, getState) => { const redirectUri = getState().auth.toJS().redirectUri; dispatch(signUpRequest()); coralApi('/users', { method: 'POST', body: formData, headers: {'X-Pym-Url': redirectUri} }) .then(({user}) => { dispatch(signUpSuccess(user)); }) .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(signUpFailure(errorMessage)); }); }; //============================================================================== // FORGOT PASSWORD //============================================================================== const forgotPasswordRequest = () => ({ type: actions.FETCH_FORGOT_PASSWORD_REQUEST }); const forgotPasswordSuccess = () => ({ type: actions.FETCH_FORGOT_PASSWORD_SUCCESS }); const forgotPasswordFailure = (error) => ({ type: actions.FETCH_FORGOT_PASSWORD_FAILURE, error, }); export const fetchForgotPassword = (email) => (dispatch, getState) => { dispatch(forgotPasswordRequest(email)); const redirectUri = getState().auth.toJS().redirectUri; coralApi('/account/password/reset', { method: 'POST', body: {email, loc: redirectUri} }) .then(() => dispatch(forgotPasswordSuccess())) .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(forgotPasswordFailure(errorMessage)); }); }; //============================================================================== // LOGOUT //============================================================================== export const logout = () => (dispatch) => { return coralApi('/auth', {method: 'DELETE'}).then(() => { if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } // Reset the websocket. resetWebsocket(); dispatch({type: actions.LOGOUT}); pym.sendMessage('coral-auth-changed'); }); }; //============================================================================== // CHECK LOGIN //============================================================================== const checkLoginRequest = () => ({type: actions.CHECK_LOGIN_REQUEST}); const checkLoginFailure = (error) => ({type: actions.CHECK_LOGIN_FAILURE, error}); const checkLoginSuccess = (user, isAdmin) => ({ type: actions.CHECK_LOGIN_SUCCESS, user, isAdmin }); export const checkLogin = () => (dispatch) => { dispatch(checkLoginRequest()); coralApi('/auth') .then((result) => { if (!result.user) { if (!bowser.safari && !bowser.ios) { Storage.removeItem('token'); } throw new Error('Not logged in'); } // Reset the websocket. resetWebsocket(); dispatch(checkLoginSuccess(result.user)); pym.sendMessage('coral-auth-changed', JSON.stringify(result.user)); // Display create username dialog if necessary. if (result.user.canEditName && result.user.status !== 'BANNED') { dispatch(showCreateUsernameDialog()); } }) .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(checkLoginFailure(errorMessage)); }); }; export const validForm = () => ({type: actions.VALID_FORM}); export const invalidForm = (error) => ({type: actions.INVALID_FORM, error}); //============================================================================== // VERIFY EMAIL //============================================================================== const verifyEmailRequest = () => ({ type: actions.VERIFY_EMAIL_REQUEST }); const verifyEmailSuccess = () => ({ type: actions.VERIFY_EMAIL_SUCCESS }); const verifyEmailFailure = () => ({ type: actions.VERIFY_EMAIL_FAILURE }); export const requestConfirmEmail = (email) => (dispatch, getState) => { const redirectUri = getState().auth.toJS().redirectUri; dispatch(verifyEmailRequest()); return coralApi('/users/resend-verify', { method: 'POST', body: {email}, headers: {'X-Pym-Url': redirectUri} }) .then(() => { dispatch(verifyEmailSuccess()); }) .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); dispatch(verifyEmailFailure(errorMessage)); }); }; // Login Popup actions. export const setRequireEmailVerification = (required) => ({ type: actions.SET_REQUIRE_EMAIL_VERIFICATION, required, }); export const setRedirectUri = (uri) => ({ type: actions.SET_REDIRECT_URI, uri, });