diff --git a/.eslintignore b/.eslintignore
index 5be504ff4..6dc63cb59 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -13,6 +13,7 @@ public
!plugins/talk-plugin-facebook-auth
!plugins/talk-plugin-featured-comments
!plugins/talk-plugin-flag-details
+!plugins/talk-plugin-google-auth
!plugins/talk-plugin-ignore-user
!plugins/talk-plugin-like
!plugins/talk-plugin-love
@@ -21,6 +22,7 @@ public
!plugins/talk-plugin-moderation-actions
!plugins/talk-plugin-offtopic
!plugins/talk-plugin-permalink
+!plugins/talk-plugin-profile-settings
!plugins/talk-plugin-remember-sort
!plugins/talk-plugin-respect
!plugins/talk-plugin-sort-most-liked
@@ -31,5 +33,4 @@ public
!plugins/talk-plugin-sort-oldest
!plugins/talk-plugin-subscriber
!plugins/talk-plugin-toxic-comments
-!plugins/talk-plugin-viewing-options
-!plugins/talk-plugin-profile-settings
+!plugins/talk-plugin-viewing-options
\ No newline at end of file
diff --git a/client/coral-auth-callback/src/index.js b/client/coral-auth-callback/src/index.js
index b7f3ed226..41a83dcd2 100644
--- a/client/coral-auth-callback/src/index.js
+++ b/client/coral-auth-callback/src/index.js
@@ -1,14 +1,34 @@
+import { HANDLE_SUCCESSFUL_LOGIN } from 'coral-framework/constants/auth';
+import { getStaticConfiguration } from 'coral-framework/services/staticConfiguration';
+import { createPostMessage } from 'coral-framework/services/postMessage';
+
document.addEventListener('DOMContentLoaded', () => {
- // Get the auth element and parse it as JSON by decoding it.
- const auth = document.getElementById('auth');
- const doc = document.implementation.createHTMLDocument('');
- doc.body.innerHTML = auth.innerText;
+ try {
+ const staticConfig = getStaticConfiguration();
+ const { STATIC_ORIGIN: origin } = staticConfig;
+ const postMessage = createPostMessage(origin);
- // Set the item in localStorage.
- localStorage.setItem('auth', doc.body.textContent);
+ // Get the auth element and parse it as JSON by decoding it.
+ const auth = document.getElementById('auth');
+ const doc = document.implementation.createHTMLDocument('');
+ doc.body.innerHTML = auth.innerText;
- // Close the window.
- setTimeout(() => {
- window.close();
- }, 50);
+ // Auth state is contained within the node.
+ const { err, data } = JSON.parse(doc.body.textContent);
+ if (err) {
+ // TODO: send back the error message.
+ console.error(err);
+ } else {
+ // The data will contain a user and a token.
+ const { user, token } = data;
+
+ // Send the state back.
+ postMessage.post(HANDLE_SUCCESSFUL_LOGIN, { user, token });
+ }
+ } finally {
+ // Always close the window.
+ setTimeout(() => {
+ window.close();
+ }, 50);
+ }
});
diff --git a/client/coral-embed-stream/src/actions/asset.js b/client/coral-embed-stream/src/actions/asset.js
deleted file mode 100644
index 158e65594..000000000
--- a/client/coral-embed-stream/src/actions/asset.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as actions from '../constants/asset';
-import { notify } from 'coral-framework/actions/notification';
-
-import t from 'coral-framework/services/i18n';
-
-export const fetchAssetRequest = () => ({ type: actions.FETCH_ASSET_REQUEST });
-export const fetchAssetSuccess = asset => ({
- type: actions.FETCH_ASSET_SUCCESS,
- asset,
-});
-export const fetchAssetFailure = error => ({
- type: actions.FETCH_ASSET_FAILURE,
- error,
-});
-
-const updateAssetSettingsRequest = () => ({
- type: actions.UPDATE_ASSET_SETTINGS_REQUEST,
-});
-const updateAssetSettingsSuccess = settings => ({
- type: actions.UPDATE_ASSET_SETTINGS_SUCCESS,
- settings,
-});
-const updateAssetSettingsFailure = error => ({
- type: actions.UPDATE_ASSET_SETTINGS_FAILURE,
- error,
-});
-
-export const updateConfiguration = newConfig => (
- dispatch,
- getState,
- { rest }
-) => {
- const assetId = getState().asset.id;
- dispatch(updateAssetSettingsRequest());
- rest(`/assets/${assetId}/settings`, { method: 'PUT', body: newConfig })
- .then(() => {
- dispatch(notify('success', t('framework.success_update_settings')));
- dispatch(updateAssetSettingsSuccess(newConfig));
- })
- .catch(error => {
- console.error(error);
- dispatch(updateAssetSettingsFailure(error));
- });
-};
-
-export const updateOpenStream = closedBody => (
- dispatch,
- getState,
- { rest }
-) => {
- const assetId = getState().asset.id;
- dispatch(fetchAssetRequest());
- rest(`/assets/${assetId}/status`, { method: 'PUT', body: closedBody })
- .then(() => {
- dispatch(notify('success', t('framework.success_update_settings')));
- dispatch(fetchAssetSuccess(closedBody));
- })
- .catch(error => {
- console.error(error);
- dispatch(fetchAssetFailure(error));
- });
-};
-
-const openStream = () => ({ type: actions.OPEN_COMMENTS });
-const closeStream = () => ({ type: actions.CLOSE_COMMENTS });
-
-export const updateOpenStatus = status => dispatch => {
- if (status === 'open') {
- dispatch(openStream());
- dispatch(updateOpenStream({ closedAt: null }));
- } else {
- dispatch(closeStream());
- dispatch(updateOpenStream({ closedAt: new Date().getTime() }));
- }
-};
diff --git a/client/coral-embed-stream/src/actions/config.js b/client/coral-embed-stream/src/actions/config.js
deleted file mode 100644
index e30dc24cf..000000000
--- a/client/coral-embed-stream/src/actions/config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import { ADD_EXTERNAL_CONFIG } from '../constants/config';
-
-export const addExternalConfig = config => ({
- type: ADD_EXTERNAL_CONFIG,
- config,
-});
diff --git a/client/coral-embed-stream/src/actions/login.js b/client/coral-embed-stream/src/actions/login.js
index c602bf7b6..fe1f76149 100644
--- a/client/coral-embed-stream/src/actions/login.js
+++ b/client/coral-embed-stream/src/actions/login.js
@@ -1,14 +1,10 @@
import * as actions from '../constants/login';
-import { checkLogin } from 'coral-framework/actions/auth';
export const showSignInDialog = () => ({
type: actions.SHOW_SIGNIN_DIALOG,
});
-export const hideSignInDialog = () => dispatch => {
- dispatch(checkLogin());
- dispatch({ type: actions.HIDE_SIGNIN_DIALOG });
-};
+export const hideSignInDialog = () => ({ type: actions.HIDE_SIGNIN_DIALOG });
export const focusSignInDialog = () => ({
type: actions.FOCUS_SIGNIN_DIALOG,
diff --git a/client/coral-embed-stream/src/constants/asset.js b/client/coral-embed-stream/src/constants/asset.js
deleted file mode 100644
index 40f746706..000000000
--- a/client/coral-embed-stream/src/constants/asset.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export const FETCH_ASSET_REQUEST = 'FETCH_ASSET_REQUEST';
-export const FETCH_ASSET_FAILURE = 'FETCH_ASSET_FAILURE';
-export const FETCH_ASSET_SUCCESS = 'FETCH_ASSET_SUCCESS';
-
-export const UPDATE_ASSET_SETTINGS_REQUEST = 'UPDATE_ASSET_SETTINGS_REQUEST';
-export const UPDATE_ASSET_SETTINGS_SUCCESS = 'UPDATE_ASSET_SETTINGS_SUCCESS';
-export const UPDATE_ASSET_SETTINGS_FAILURE = 'UPDATE_ASSET_SETTINGS_FAILURE';
-
-export const OPEN_COMMENTS = 'OPEN_COMMENTS';
-export const CLOSE_COMMENTS = 'CLOSE_COMMENTS';
diff --git a/client/coral-embed-stream/src/constants/config.js b/client/coral-embed-stream/src/constants/config.js
deleted file mode 100644
index 5821316c5..000000000
--- a/client/coral-embed-stream/src/constants/config.js
+++ /dev/null
@@ -1 +0,0 @@
-export const ADD_EXTERNAL_CONFIG = 'ADD_EXTERNAL_CONFIG';
diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js
index f8486fd14..d365af50d 100644
--- a/client/coral-embed-stream/src/containers/Embed.js
+++ b/client/coral-embed-stream/src/containers/Embed.js
@@ -2,7 +2,6 @@ import React from 'react';
import { compose, gql } from 'react-apollo';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
-import isEqual from 'lodash/isEqual';
import get from 'lodash/get';
import branch from 'recompose/branch';
import renderComponent from 'recompose/renderComponent';
@@ -14,12 +13,11 @@ import {
hideSignInDialog,
} from '../actions/login';
import { updateStatus } from 'coral-framework/actions/auth';
-import { fetchAssetSuccess } from '../actions/asset';
import {
getDefinitionName,
getSlotFragmentSpreads,
} from 'coral-framework/utils';
-import { withQuery } from 'coral-framework/hocs';
+import { withQuery, withPopupAuthHandler } from 'coral-framework/hocs';
import Embed from '../components/Embed';
import Stream from '../tabs/stream/containers/Stream';
import AutomaticAssetClosure from './AutomaticAssetClosure';
@@ -107,12 +105,6 @@ class EmbedContainer extends React.Component {
this.props.data.refetch();
this.resubscribe(nextProps);
}
-
- const { fetchAssetSuccess } = this.props;
- if (!isEqual(nextProps.root.asset, this.props.root.asset)) {
- // TODO: remove asset data from redux store.
- fetchAssetSuccess(nextProps.root.asset);
- }
}
componentDidUpdate(prevProps) {
@@ -298,7 +290,6 @@ EmbedContainer.propTypes = {
activeTab: PropTypes.string,
parentUrl: PropTypes.string,
data: PropTypes.object,
- fetchAssetSuccess: PropTypes.func,
showSignInDialog: PropTypes.bool,
signInDialogFocus: PropTypes.bool,
};
@@ -322,7 +313,6 @@ const mapDispatchToProps = dispatch =>
bindActionCreators(
{
setActiveTab,
- fetchAssetSuccess,
notify,
focusSignInDialog,
blurSignInDialog,
@@ -333,6 +323,7 @@ const mapDispatchToProps = dispatch =>
);
export default compose(
+ withPopupAuthHandler,
connect(mapStateToProps, mapDispatchToProps),
branch(props => !props.checkedInitialLogin, renderComponent(Spinner)),
withEmbedQuery
diff --git a/client/coral-embed-stream/src/reducers/asset.js b/client/coral-embed-stream/src/reducers/asset.js
deleted file mode 100644
index b683eae30..000000000
--- a/client/coral-embed-stream/src/reducers/asset.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as actions from '../constants/asset';
-
-const initialState = {
- closedAt: null,
- settings: null,
- title: null,
- url: null,
- features: {},
- status: 'open',
- moderation: null,
-};
-
-export default function asset(state = initialState, action) {
- switch (action.type) {
- case actions.FETCH_ASSET_SUCCESS:
- return {
- ...state,
- ...action.asset,
- };
- case actions.UPDATE_ASSET_SETTINGS_SUCCESS:
- return {
- ...state,
- settings: action.settings,
- };
- default:
- return state;
- }
-}
diff --git a/client/coral-embed-stream/src/reducers/config.js b/client/coral-embed-stream/src/reducers/config.js
deleted file mode 100644
index 09a89a743..000000000
--- a/client/coral-embed-stream/src/reducers/config.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { ADD_EXTERNAL_CONFIG } from '../constants/config';
-
-const initialState = {};
-
-export default function config(state = initialState, action) {
- switch (action.type) {
- case ADD_EXTERNAL_CONFIG:
- return {
- ...state,
- ...action.config,
- };
- default:
- return state;
- }
-}
diff --git a/client/coral-embed-stream/src/reducers/index.js b/client/coral-embed-stream/src/reducers/index.js
index f8eed72d4..15056d414 100644
--- a/client/coral-embed-stream/src/reducers/index.js
+++ b/client/coral-embed-stream/src/reducers/index.js
@@ -1,5 +1,4 @@
import login from './login';
-import asset from './asset';
import embed from './embed';
import configure from './configure';
import stream from './stream';
@@ -7,7 +6,6 @@ import profile from './profile';
export default {
login,
- asset,
embed,
configure,
stream,
diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js
index 0c4780548..17b1c38f9 100644
--- a/client/coral-framework/actions/auth.js
+++ b/client/coral-framework/actions/auth.js
@@ -63,13 +63,25 @@ export const setAuthToken = token => (dispatch, _, { localStorage }) => {
export const handleSuccessfulLogin = (user, token) => (
dispatch,
_,
- { client, localStorage }
+ { client, localStorage, postMessage }
) => {
+ const { exp } = jwtDecode(token);
+
if (localStorage) {
- localStorage.setItem('exp', jwtDecode(token).exp);
+ localStorage.setItem('exp', exp);
localStorage.setItem('token', token);
}
+ // Send the message via the messages service to the window.opener if it
+ // exists.
+ if (window.opener) {
+ postMessage.post(
+ actions.HANDLE_SUCCESSFUL_LOGIN,
+ { user, token },
+ window.opener
+ );
+ }
+
client.resetWebsocket();
dispatch({
diff --git a/client/coral-framework/components/TalkProvider.js b/client/coral-framework/components/TalkProvider.js
index 3164d8e9d..343d559ea 100644
--- a/client/coral-framework/components/TalkProvider.js
+++ b/client/coral-framework/components/TalkProvider.js
@@ -17,6 +17,7 @@ class TalkProvider extends React.Component {
store: this.props.store,
pymLocalStorage: this.props.pymLocalStorage,
pymSessionStorage: this.props.pymSessionStorage,
+ postMessage: this.props.postMessage,
};
}
@@ -39,6 +40,7 @@ TalkProvider.childContextTypes = {
pymSessionStorage: PropTypes.object,
history: PropTypes.object,
store: PropTypes.object,
+ postMessage: PropTypes.object,
};
TalkProvider.propTypes = TalkProvider.childContextTypes;
diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js
index f34a2cc88..1b3b4c6fc 100644
--- a/client/coral-framework/graphql/mutations.js
+++ b/client/coral-framework/graphql/mutations.js
@@ -698,7 +698,7 @@ export const withCloseAsset = withMutation(
const fragmentId = `Asset_${id}`;
const data = {
__typename: 'Asset',
- closedAt: new Date(),
+ closedAt: new Date().toISOString(),
isClosed: true,
};
proxy.writeFragment({ fragment, id: fragmentId, data });
diff --git a/client/coral-framework/hocs/index.js b/client/coral-framework/hocs/index.js
index eb481de99..5e2e3dd40 100644
--- a/client/coral-framework/hocs/index.js
+++ b/client/coral-framework/hocs/index.js
@@ -10,6 +10,7 @@ export { default as withSignIn } from './withSignIn';
export { default as withSignUp } from './withSignUp';
export { default as withForgotPassword } from './withForgotPassword';
export { default as withSetUsername } from './withSetUsername';
+export { default as withPopupAuthHandler } from './withPopupAuthHandler';
export {
default as withResendEmailConfirmation,
} from './withResendEmailConfirmation';
diff --git a/client/coral-framework/hocs/withPopupAuthHandler.js b/client/coral-framework/hocs/withPopupAuthHandler.js
new file mode 100644
index 000000000..ab2530e66
--- /dev/null
+++ b/client/coral-framework/hocs/withPopupAuthHandler.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import hoistStatics from 'recompose/hoistStatics';
+import PropTypes from 'prop-types';
+import { HANDLE_SUCCESSFUL_LOGIN } from 'coral-framework/constants/auth';
+import {
+ handleSuccessfulLogin,
+ setAuthToken,
+} from 'coral-framework/actions/auth';
+
+/**
+ * WithPopupAuthHandler listens to successful logins over
+ * the `postMessage` service.
+ */
+export default hoistStatics(WrappedComponent => {
+ class WithPopupAuthHandler extends React.Component {
+ static contextTypes = {
+ store: PropTypes.object,
+ postMessage: PropTypes.object,
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ context.postMessage.subscribe(this.handleAuth);
+ }
+
+ componentWillUnmount() {
+ this.context.postMessage.unsubscribe(this.handleAuth);
+ }
+
+ handleAuth = ({ name, data }) => {
+ if (name !== HANDLE_SUCCESSFUL_LOGIN) {
+ return;
+ }
+
+ const { store } = this.context;
+ // data will contain the user and token.
+ const { user, token } = data;
+
+ if (user && token) {
+ store.dispatch(handleSuccessfulLogin(user, token));
+ } else if (token) {
+ store.dispatch(setAuthToken(token));
+ } else {
+ console.error('Invalid auth data supplied', data);
+ }
+ };
+
+ render() {
+ return