diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/assets.js
index b9ebe0ada..1ce6ad6ac 100644
--- a/client/coral-admin/src/actions/assets.js
+++ b/client/coral-admin/src/actions/assets.js
@@ -8,7 +8,6 @@ import {
UPDATE_ASSETS
} from '../constants/assets';
-import coralApi from '../../../coral-framework/helpers/request';
import t from 'coral-framework/services/i18n';
/**
@@ -17,9 +16,9 @@ import t from 'coral-framework/services/i18n';
// Fetch a page of assets
// Get comments to fill each of the three lists on the mod queue
-export const fetchAssets = (skip = '', limit = '', search = '', sort = '', filter = '') => (dispatch) => {
+export const fetchAssets = (skip = '', limit = '', search = '', sort = '', filter = '') => (dispatch, _, {rest}) => {
dispatch({type: FETCH_ASSETS_REQUEST});
- return coralApi(`/assets?skip=${skip}&limit=${limit}&sort=${sort}&search=${search}&filter=${filter}`)
+ return rest(`/assets?skip=${skip}&limit=${limit}&sort=${sort}&search=${search}&filter=${filter}`)
.then(({result, count}) =>
dispatch({type: FETCH_ASSETS_SUCCESS,
assets: result,
@@ -34,9 +33,9 @@ export const fetchAssets = (skip = '', limit = '', search = '', sort = '', filte
// Update an asset state
// Get comments to fill each of the three lists on the mod queue
-export const updateAssetState = (id, closedAt) => (dispatch) => {
+export const updateAssetState = (id, closedAt) => (dispatch, _, {rest}) => {
dispatch({type: UPDATE_ASSET_STATE_REQUEST, id, closedAt});
- return coralApi(`/assets/${id}/status`, {method: 'PUT', body: {closedAt}})
+ return rest(`/assets/${id}/status`, {method: 'PUT', body: {closedAt}})
.then(() => dispatch({type: UPDATE_ASSET_STATE_SUCCESS}))
.catch((error) => {
console.error(error);
diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js
index 8c84a95ec..3433fc95a 100644
--- a/client/coral-admin/src/actions/auth.js
+++ b/client/coral-admin/src/actions/auth.js
@@ -1,8 +1,6 @@
import bowser from 'bowser';
import * as actions from '../constants/auth';
-import coralApi from 'coral-framework/helpers/request';
import * as Storage from 'coral-framework/helpers/storage';
-import {resetWebsocket} from 'coral-framework/services/client';
import t from 'coral-framework/services/i18n';
import jwtDecode from 'jwt-decode';
@@ -10,7 +8,7 @@ import jwtDecode from 'jwt-decode';
// SIGN IN
//==============================================================================
-export const handleLogin = (email, password, recaptchaResponse) => (dispatch) => {
+export const handleLogin = (email, password, recaptchaResponse) => (dispatch, _, {rest, client}) => {
dispatch({type: actions.LOGIN_REQUEST});
const params = {
@@ -27,7 +25,7 @@ export const handleLogin = (email, password, recaptchaResponse) => (dispatch) =>
};
}
- return coralApi('/auth/local', params)
+ return rest('/auth/local', params)
.then(({user, token}) => {
if (!user) {
@@ -38,7 +36,7 @@ export const handleLogin = (email, password, recaptchaResponse) => (dispatch) =>
}
dispatch(handleAuthToken(token));
- resetWebsocket();
+ client.resetWebsocket();
dispatch(checkLoginSuccess(user));
})
.catch((error) => {
@@ -84,11 +82,11 @@ const forgotPasswordFailure = (error) => ({
error,
});
-export const requestPasswordReset = (email) => (dispatch) => {
+export const requestPasswordReset = (email) => (dispatch, _, {rest}) => {
dispatch(forgotPasswordRequest(email));
const redirectUri = location.href;
- return coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}})
+ return rest('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}})
.then(() => dispatch(forgotPasswordSuccess()))
.catch((error) => {
console.error(error);
@@ -116,9 +114,9 @@ const checkLoginFailure = (error) => ({
error
});
-export const checkLogin = () => (dispatch) => {
+export const checkLogin = () => (dispatch, _, {rest, client}) => {
dispatch(checkLoginRequest());
- return coralApi('/auth')
+ return rest('/auth')
.then(({user}) => {
if (!user) {
if (!bowser.safari && !bowser.ios) {
@@ -127,7 +125,7 @@ export const checkLogin = () => (dispatch) => {
return dispatch(checkLoginFailure('not logged in'));
}
- resetWebsocket();
+ client.resetWebsocket();
dispatch(checkLoginSuccess(user));
})
.catch((error) => {
@@ -141,12 +139,12 @@ export const checkLogin = () => (dispatch) => {
// LOGOUT
//==============================================================================
-export const logout = () => (dispatch) => {
- return coralApi('/auth', {method: 'DELETE'}).then(() => {
+export const logout = () => (dispatch, _, {rest, client}) => {
+ return rest('/auth', {method: 'DELETE'}).then(() => {
Storage.removeItem('token');
// Reset the websocket.
- resetWebsocket();
+ client.resetWebsocket();
dispatch({type: actions.LOGOUT});
});
diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js
index 70c9b7592..402a32e5d 100644
--- a/client/coral-admin/src/actions/community.js
+++ b/client/coral-admin/src/actions/community.js
@@ -14,13 +14,12 @@ import {
HIDE_REJECT_USERNAME_DIALOG
} from '../constants/community';
-import coralApi from '../../../coral-framework/helpers/request';
import t from 'coral-framework/services/i18n';
-export const fetchAccounts = (query = {}) => (dispatch) => {
+export const fetchAccounts = (query = {}) => (dispatch, _, {rest}) => {
dispatch(requestFetchAccounts());
- coralApi(`/users?${qs.stringify(query)}`)
+ rest(`/users?${qs.stringify(query)}`)
.then(({result, page, count, limit, totalPages}) =>{
dispatch({
type: FETCH_COMMENTERS_SUCCESS,
@@ -51,15 +50,15 @@ export const newPage = () => ({
type: COMMENTERS_NEW_PAGE
});
-export const setRole = (id, role) => (dispatch) => {
- return coralApi(`/users/${id}/role`, {method: 'POST', body: {role}})
+export const setRole = (id, role) => (dispatch, _, {rest}) => {
+ return rest(`/users/${id}/role`, {method: 'POST', body: {role}})
.then(() => {
return dispatch({type: SET_ROLE, id, role});
});
};
-export const setCommenterStatus = (id, status) => (dispatch) => {
- return coralApi(`/users/${id}/status`, {method: 'POST', body: {status}})
+export const setCommenterStatus = (id, status) => (dispatch, _, {rest}) => {
+ return rest(`/users/${id}/status`, {method: 'POST', body: {status}})
.then(() => {
return dispatch({type: SET_COMMENTER_STATUS, id, status});
});
diff --git a/client/coral-admin/src/actions/install.js b/client/coral-admin/src/actions/install.js
index 6393c58dd..d7210782a 100644
--- a/client/coral-admin/src/actions/install.js
+++ b/client/coral-admin/src/actions/install.js
@@ -1,4 +1,3 @@
-import coralApi from 'coral-framework/helpers/request';
import * as actions from '../constants/install';
import validate from 'coral-framework/helpers/validate';
import errorMsj from 'coral-framework/helpers/error';
@@ -106,10 +105,10 @@ export const submitUser = () => (dispatch, getState) => {
});
};
-export const finishInstall = () => (dispatch, getState) => {
+export const finishInstall = () => (dispatch, getState, {rest}) => {
const data = getState().install.data;
dispatch(installRequest());
- return coralApi('/setup', {method: 'POST', body: data})
+ return rest('/setup', {method: 'POST', body: data})
.then(() => {
dispatch(installSuccess());
dispatch(nextStep());
@@ -129,9 +128,9 @@ const checkInstallRequest = () => ({type: actions.CHECK_INSTALL_REQUEST});
const checkInstallSuccess = (installed) => ({type: actions.CHECK_INSTALL_SUCCESS, installed});
const checkInstallFailure = (error) => ({type: actions.CHECK_INSTALL_FAILURE, error});
-export const checkInstall = (next) => (dispatch) => {
+export const checkInstall = (next) => (dispatch, _, {rest}) => {
dispatch(checkInstallRequest());
- coralApi('/setup')
+ rest('/setup')
.then(({installed}) => {
dispatch(checkInstallSuccess(installed));
if (installed) {
diff --git a/client/coral-admin/src/actions/settings.js b/client/coral-admin/src/actions/settings.js
index ca9062b9e..35f2013c9 100644
--- a/client/coral-admin/src/actions/settings.js
+++ b/client/coral-admin/src/actions/settings.js
@@ -1,4 +1,3 @@
-import coralApi from '../../../coral-framework/helpers/request';
import t from 'coral-framework/services/i18n';
export const SETTINGS_LOADING = 'SETTINGS_LOADING';
@@ -14,9 +13,9 @@ export const SAVE_SETTINGS_FAILED = 'SAVE_SETTINGS_FAILED';
export const WORDLIST_UPDATED = 'WORDLIST_UPDATED';
export const DOMAINLIST_UPDATED = 'DOMAINLIST_UPDATED';
-export const fetchSettings = () => (dispatch) => {
+export const fetchSettings = () => (dispatch, _, {rest}) => {
dispatch({type: SETTINGS_LOADING});
- coralApi('/settings')
+ rest('/settings')
.then((settings) => {
dispatch({type: SETTINGS_RECEIVED, settings});
})
@@ -41,13 +40,13 @@ export const updateDomainlist = (listName, list) => {
return {type: DOMAINLIST_UPDATED, listName, list};
};
-export const saveSettingsToServer = () => (dispatch, getState) => {
+export const saveSettingsToServer = () => (dispatch, getState, {rest}) => {
let settings = getState().settings;
if (settings.charCount) {
settings.charCount = parseInt(settings.charCount);
}
dispatch({type: SAVE_SETTINGS_LOADING});
- coralApi('/settings', {method: 'PUT', body: settings})
+ rest('/settings', {method: 'PUT', body: settings})
.then(() => {
dispatch({type: SAVE_SETTINGS_SUCCESS, settings});
})
diff --git a/client/coral-admin/src/actions/users.js b/client/coral-admin/src/actions/users.js
index 7ba051a90..994a79a79 100644
--- a/client/coral-admin/src/actions/users.js
+++ b/client/coral-admin/src/actions/users.js
@@ -1,4 +1,3 @@
-import coralApi from '../../../coral-framework/helpers/request';
import * as userTypes from '../constants/users';
import t from 'coral-framework/services/i18n';
@@ -7,9 +6,9 @@ import t from 'coral-framework/services/i18n';
*/
// change status of a user
export const userStatusUpdate = (status, userId, commentId) => {
- return (dispatch) => {
+ return (dispatch, _, {rest}) => {
dispatch({type: userTypes.UPDATE_STATUS_REQUEST});
- return coralApi(`/users/${userId}/status`, {method: 'POST', body: {status: status, comment_id: commentId}})
+ return rest(`/users/${userId}/status`, {method: 'POST', body: {status: status, comment_id: commentId}})
.then((res) => dispatch({type: userTypes.UPDATE_STATUS_SUCCESS, res}))
.catch((error) => {
console.error(error);
@@ -21,8 +20,8 @@ export const userStatusUpdate = (status, userId, commentId) => {
// change status of a user
export const sendNotificationEmail = (userId, subject, body) => {
- return (dispatch) => {
- return coralApi(`/users/${userId}/email`, {method: 'POST', body: {subject, body}})
+ return (dispatch, _, {rest}) => {
+ return rest(`/users/${userId}/email`, {method: 'POST', body: {subject, body}})
.catch((error) => {
console.error(error);
const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString();
@@ -33,8 +32,8 @@ export const sendNotificationEmail = (userId, subject, body) => {
// let a user edit their username
export const enableUsernameEdit = (userId) => {
- return (dispatch) => {
- return coralApi(`/users/${userId}/username-enable`, {method: 'POST'})
+ return (dispatch, _, {rest}) => {
+ return rest(`/users/${userId}/username-enable`, {method: 'POST'})
.catch((error) => {
console.error(error);
const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString();
diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js
index ed9d537c0..e85cbed8c 100644
--- a/client/coral-admin/src/index.js
+++ b/client/coral-admin/src/index.js
@@ -1,34 +1,21 @@
import React from 'react';
import {render} from 'react-dom';
import smoothscroll from 'smoothscroll-polyfill';
-import EventEmitter from 'eventemitter2';
import TalkProvider from 'coral-framework/components/TalkProvider';
-
-import {getClient} from 'coral-framework/services/client';
-import store from './services/store';
-
+import {createContext} from 'coral-framework/services/bootstrap';
+import reducers from './reducers';
import App from './components/App';
-
import 'react-mdl/extra/material.js';
import './graphql';
-import {loadPluginsTranslations, injectPluginsReducers} from 'coral-framework/helpers/plugins';
import plugins from 'pluginsConfig';
-const eventEmitter = new EventEmitter();
-const client = getClient();
+const context = createContext(reducers, plugins);
-// TODO: pass redux actions through the emitter.
-
-loadPluginsTranslations();
-injectPluginsReducers();
smoothscroll.polyfill();
render(
diff --git a/client/coral-embed-stream/src/actions/asset.js b/client/coral-embed-stream/src/actions/asset.js
index 87b6fdd98..6acebeafb 100644
--- a/client/coral-embed-stream/src/actions/asset.js
+++ b/client/coral-embed-stream/src/actions/asset.js
@@ -1,5 +1,4 @@
import * as actions from '../constants/asset';
-import coralApi from 'coral-framework/helpers/request';
import {addNotification} from 'coral-framework/actions/notification';
import t from 'coral-framework/services/i18n';
@@ -12,10 +11,10 @@ const updateAssetSettingsRequest = () => ({type: actions.UPDATE_ASSET_SETTINGS_R
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) => {
+export const updateConfiguration = (newConfig) => (dispatch, getState, {rest}) => {
const assetId = getState().asset.id;
dispatch(updateAssetSettingsRequest());
- coralApi(`/assets/${assetId}/settings`, {method: 'PUT', body: newConfig})
+ rest(`/assets/${assetId}/settings`, {method: 'PUT', body: newConfig})
.then(() => {
dispatch(addNotification('success', t('framework.success_update_settings')));
dispatch(updateAssetSettingsSuccess(newConfig));
@@ -26,10 +25,10 @@ export const updateConfiguration = (newConfig) => (dispatch, getState) => {
});
};
-export const updateOpenStream = (closedBody) => (dispatch, getState) => {
+export const updateOpenStream = (closedBody) => (dispatch, getState, {rest}) => {
const assetId = getState().asset.id;
dispatch(fetchAssetRequest());
- coralApi(`/assets/${assetId}/status`, {method: 'PUT', body: closedBody})
+ rest(`/assets/${assetId}/status`, {method: 'PUT', body: closedBody})
.then(() => {
dispatch(addNotification('success', t('framework.success_update_settings')));
dispatch(fetchAssetSuccess(closedBody));
diff --git a/client/coral-embed-stream/src/actions/auth.js b/client/coral-embed-stream/src/actions/auth.js
index 851889a9b..55f7eb546 100644
--- a/client/coral-embed-stream/src/actions/auth.js
+++ b/client/coral-embed-stream/src/actions/auth.js
@@ -2,11 +2,8 @@ import jwtDecode from 'jwt-decode';
import bowser from 'bowser';
import * as actions from '../constants/auth';
import * as Storage from 'coral-framework/helpers/storage';
-import coralApi, {base} from 'coral-framework/helpers/request';
-import pym from 'coral-framework/services/pym';
import {addNotification} from 'coral-framework/actions/notification';
-import {resetWebsocket} from 'coral-framework/services/client';
import t from 'coral-framework/services/i18n';
export const showSignInDialog = () => ({
@@ -59,9 +56,9 @@ export const updateUsername = ({username}) => ({
username
});
-export const createUsername = (userId, formData) => (dispatch) => {
+export const createUsername = (userId, formData) => (dispatch, _, {rest}) => {
dispatch(createUsernameRequest());
- coralApi('/account/username', {method: 'PUT', body: formData})
+ rest('/account/username', {method: 'PUT', body: formData})
.then(() => {
dispatch(createUsernameSuccess());
dispatch(hideCreateUsernameDialog());
@@ -123,10 +120,10 @@ export const handleAuthToken = (token) => (dispatch) => {
//==============================================================================
export const fetchSignIn = (formData) => {
- return (dispatch) => {
+ return (dispatch, _, {rest}) => {
dispatch(signInRequest());
- return coralApi('/auth/local', {method: 'POST', body: formData})
+ return rest('/auth/local', {method: 'POST', body: formData})
.then(({token}) => {
if (!bowser.safari && !bowser.ios) {
dispatch(handleAuthToken(token));
@@ -171,10 +168,10 @@ const signInFacebookFailure = (error) => ({
error
});
-export const fetchSignInFacebook = () => (dispatch) => {
+export const fetchSignInFacebook = () => (dispatch, _, {rest}) => {
dispatch(signInFacebookRequest());
window.open(
- `${base}/auth/facebook`,
+ `${rest.uri}/auth/facebook`,
'Continue with Facebook',
'menubar=0,resizable=0,width=500,height=500,top=200,left=500'
);
@@ -188,10 +185,10 @@ const signUpFacebookRequest = () => ({
type: actions.FETCH_SIGNUP_FACEBOOK_REQUEST
});
-export const fetchSignUpFacebook = () => (dispatch) => {
+export const fetchSignUpFacebook = () => (dispatch, _, {rest}) => {
dispatch(signUpFacebookRequest());
window.open(
- `${base}/auth/facebook`,
+ `${rest.uri}/auth/facebook`,
'Continue with Facebook',
'menubar=0,resizable=0,width=500,height=500,top=200,left=500'
);
@@ -220,11 +217,11 @@ 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) => {
+export const fetchSignUp = (formData) => (dispatch, getState, {rest}) => {
const redirectUri = getState().auth.redirectUri;
dispatch(signUpRequest());
- coralApi('/users', {
+ rest('/users', {
method: 'POST',
body: formData,
headers: {'X-Pym-Url': redirectUri}
@@ -256,10 +253,10 @@ const forgotPasswordFailure = (error) => ({
error,
});
-export const fetchForgotPassword = (email) => (dispatch, getState) => {
+export const fetchForgotPassword = (email) => (dispatch, getState, {rest}) => {
dispatch(forgotPasswordRequest(email));
const redirectUri = getState().auth.redirectUri;
- coralApi('/account/password/reset', {
+ rest('/account/password/reset', {
method: 'POST',
body: {email, loc: redirectUri}
})
@@ -275,16 +272,15 @@ export const fetchForgotPassword = (email) => (dispatch, getState) => {
// LOGOUT
//==============================================================================
-export const logout = () => (dispatch) => {
- return coralApi('/auth', {method: 'DELETE'}).then(() => {
- Storage.removeItem('token');
+export const logout = () => async (dispatch, _, {rest, client, pym}) => {
+ await rest('/auth', {method: 'DELETE'});
+ Storage.removeItem('token');
- // Reset the websocket.
- resetWebsocket();
+ // Reset the websocket.
+ client.resetWebsocket();
- dispatch({type: actions.LOGOUT});
- pym.sendMessage('coral-auth-changed');
- });
+ dispatch({type: actions.LOGOUT});
+ pym.sendMessage('coral-auth-changed');
};
//==============================================================================
@@ -300,9 +296,9 @@ const checkLoginSuccess = (user, isAdmin) => ({
isAdmin
});
-export const checkLogin = () => (dispatch) => {
+export const checkLogin = () => (dispatch, _, {rest, client, pym}) => {
dispatch(checkLoginRequest());
- coralApi('/auth')
+ rest('/auth')
.then((result) => {
if (!result.user) {
Storage.removeItem('token');
@@ -310,7 +306,7 @@ export const checkLogin = () => (dispatch) => {
}
// Reset the websocket.
- resetWebsocket();
+ client.resetWebsocket();
dispatch(checkLoginSuccess(result.user));
pym.sendMessage('coral-auth-changed', JSON.stringify(result.user));
@@ -351,10 +347,10 @@ const verifyEmailFailure = () => ({
type: actions.VERIFY_EMAIL_FAILURE
});
-export const requestConfirmEmail = (email) => (dispatch, getState) => {
+export const requestConfirmEmail = (email) => (dispatch, getState, {rest}) => {
const redirectUri = getState().auth.redirectUri;
dispatch(verifyEmailRequest());
- return coralApi('/users/resend-verify', {
+ return rest('/users/resend-verify', {
method: 'POST',
body: {email},
headers: {'X-Pym-Url': redirectUri}
@@ -387,8 +383,8 @@ export const setRedirectUri = (uri) => ({
const editUsernameFailure = (error) => ({type: actions.EDIT_USERNAME_FAILURE, error});
const editUsernameSuccess = () => ({type: actions.EDIT_USERNAME_SUCCESS});
-export const editName = (username) => (dispatch) => {
- return coralApi('/account/username', {method: 'PUT', body: {username}})
+export const editName = (username) => (dispatch, _, {rest}) => {
+ return rest('/account/username', {method: 'PUT', body: {username}})
.then(() => {
dispatch(editUsernameSuccess());
dispatch(addNotification('success', t('framework.success_name_update')));
diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js
index c20760259..75f353b19 100644
--- a/client/coral-embed-stream/src/actions/stream.js
+++ b/client/coral-embed-stream/src/actions/stream.js
@@ -1,11 +1,10 @@
-import pym from 'coral-framework/services/pym';
import * as actions from '../constants/stream';
import {buildUrl} from 'coral-framework/utils/url';
import queryString from 'query-string';
export const setActiveReplyBox = (id) => ({type: actions.SET_ACTIVE_REPLY_BOX, id});
-export const viewAllComments = () => {
+export const viewAllComments = () => (dispatch, _, {pym}) => {
const search = queryString.stringify({
...queryString.parse(location.search),
@@ -24,10 +23,10 @@ export const viewAllComments = () => {
pym.sendMessage('coral-view-all-comments');
} catch (e) { /* not sure if we're worried about old browsers */ }
- return {type: actions.VIEW_ALL_COMMENTS};
+ dispatch({type: actions.VIEW_ALL_COMMENTS});
};
-export const viewComment = (id) => {
+export const viewComment = (id) => (dispatch, _, {pym}) => {
const search = queryString.stringify({
...queryString.parse(location.search),
@@ -46,7 +45,7 @@ export const viewComment = (id) => {
pym.sendMessage('coral-view-comment', id);
} catch (e) { /* not sure if we're worried about old browsers */ }
- return {type: actions.VIEW_COMMENT, id};
+ dispatch({type: actions.VIEW_COMMENT, id});
};
export const addCommentClassName = (className) => ({
diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js
index a14ce8df8..ac38d2eab 100644
--- a/client/coral-embed-stream/src/index.js
+++ b/client/coral-embed-stream/src/index.js
@@ -4,24 +4,16 @@ import {render} from 'react-dom';
import {checkLogin, handleAuthToken, logout} from 'coral-embed-stream/src/actions/auth';
import './graphql';
import {addExternalConfig} from 'coral-embed-stream/src/actions/config';
-import {getStore, injectReducers, addListener} from 'coral-framework/services/store';
-import {getClient} from 'coral-framework/services/client';
-import {createReduxEmitter} from 'coral-framework/services/events';
-import pym from 'coral-framework/services/pym';
+import {createContext} from 'coral-framework/services/bootstrap';
import AppRouter from './AppRouter';
-import {loadPluginsTranslations, injectPluginsReducers} from 'coral-framework/helpers/plugins';
import reducers from './reducers';
-import EventEmitter from 'eventemitter2';
import TalkProvider from 'coral-framework/components/TalkProvider';
import plugins from 'pluginsConfig';
-const store = getStore();
-const client = getClient();
-const eventEmitter = new EventEmitter({wildcard: true});
+const context = createContext(reducers, plugins);
-loadPluginsTranslations();
-injectPluginsReducers();
-injectReducers(reducers);
+// TODO: move init code into `bootstrap` service after auth has been refactored.
+const {store, pym} = context;
function inIframe() {
try {
@@ -57,23 +49,11 @@ if (!window.opener) {
} else {
init();
}
-
- // Pass any events through our parent.
- eventEmitter.onAny((eventName, value) => {
- pym.sendMessage('event', JSON.stringify({eventName, value}));
- });
-
- // Add a redux listener to pass through all actions to our event emitter.
- addListener(createReduxEmitter(eventEmitter));
}
render(
diff --git a/client/coral-framework/components/TalkProvider.js b/client/coral-framework/components/TalkProvider.js
index 275373c26..0f44af43f 100644
--- a/client/coral-framework/components/TalkProvider.js
+++ b/client/coral-framework/components/TalkProvider.js
@@ -12,9 +12,9 @@ class TalkProvider extends React.Component {
}
render() {
- const {children, client, store, plugins} = this.props;
+ const {children, client, store} = this.props;
return (
-
+
{children}
);
diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js
index 3173115c2..b73c89c64 100644
--- a/client/coral-framework/helpers/plugins.js
+++ b/client/coral-framework/helpers/plugins.js
@@ -6,8 +6,6 @@ import flattenDeep from 'lodash/flattenDeep';
import isEmpty from 'lodash/isEmpty';
import flatten from 'lodash/flatten';
import mapValues from 'lodash/mapValues';
-import {loadTranslations} from 'coral-framework/services/i18n';
-import {injectReducers} from 'coral-framework/services/store';
import {getDisplayName} from 'coral-framework/helpers/hoc';
import camelize from './camelize';
import plugins from 'pluginsConfig';
@@ -133,23 +131,18 @@ export function getModQueueConfigs() {
.filter((o) => o));
}
-function getTranslations() {
+export function getTranslations(plugins) {
return plugins
.map((o) => o.module.translations)
.filter((o) => o);
}
-export function loadPluginsTranslations() {
- getTranslations().forEach((t) => loadTranslations(t));
-}
-
-export function injectPluginsReducers() {
- const reducers = merge(
+export function getReducers(plugins) {
+ return merge(
...plugins
.filter((o) => o.module.reducer)
.map((o) => ({[camelize(o.name)] : o.module.reducer}))
);
- injectReducers(reducers);
}
function addMetaDataToSlotComponents() {
diff --git a/client/coral-framework/helpers/request.js b/client/coral-framework/helpers/request.js
deleted file mode 100644
index ffddc9c5e..000000000
--- a/client/coral-framework/helpers/request.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import bowser from 'bowser';
-import * as Storage from './storage';
-import merge from 'lodash/merge';
-import {getStore} from 'coral-framework/services/store';
-import {BASE_PATH} from 'coral-framework/constants/url';
-
-/**
- * getAuthToken returns the active auth token or null
- * Note: this method does not have access to the cookie based token used by
- * browsers that don't allow us to use cross domain iframe local storage.
- * @return {string|null}
- */
-export const getAuthToken = () => {
- let state = getStore().getState();
-
- if (state.config.auth_token) {
-
- // if an auth_token exists in config, use it.
- return state.config.auth_token;
-
- } else if (!bowser.safari && !bowser.ios) {
-
- // Use local storage auth tokens where there's a stable api.
- return Storage.getItem('token');
- }
-
- return null;
-};
-
-const buildOptions = (inputOptions = {}) => {
- const defaultOptions = {
- method: 'GET',
- headers: {
- Accept: 'application/json',
- 'Content-Type': 'application/json',
- },
- credentials: 'same-origin'
- };
-
- let options = merge({}, defaultOptions, inputOptions);
-
- // Apply authToken header
- let authToken = getAuthToken();
- if (authToken !== null) {
- options.headers.Authorization = `Bearer ${authToken}`;
- }
-
- if (options.method.toLowerCase() !== 'get') {
- options.body = JSON.stringify(options.body);
- }
-
- return options;
-};
-
-const handleResp = (res) => {
- if (res.status > 399) {
- return res.json().then((err) => {
- let message = err.message || res.status;
- const error = new Error();
-
- if (err.error && err.error.metadata && err.error.metadata.message) {
- error.metadata = err.error.metadata.message;
- }
-
- if (err.error && err.error.translation_key) {
- error.translation_key = err.error.translation_key;
- }
-
- error.message = message;
- error.status = res.status;
- throw error;
- });
- } else if (res.status === 204) {
- return res.text();
- } else {
- return res.json();
- }
-};
-
-export const base = `${BASE_PATH}api/v1`;
-
-export default (url, options) => {
- return fetch(`${base}${url}`, buildOptions(options)).then(handleResp);
-};
diff --git a/client/coral-framework/services/bootstrap.js b/client/coral-framework/services/bootstrap.js
new file mode 100644
index 000000000..6a18695f7
--- /dev/null
+++ b/client/coral-framework/services/bootstrap.js
@@ -0,0 +1,92 @@
+import {createStore} from './store';
+import {createClient, apolloErrorReporter} from './client';
+import pym from './pym';
+import EventEmitter from 'eventemitter2';
+import {createReduxEmitter} from './events';
+import {createRestClient} from './rest';
+import thunk from 'redux-thunk';
+import {loadTranslations} from './i18n';
+import {getTranslations, getReducers} from '../helpers/plugins';
+import bowser from 'bowser';
+import * as Storage from '../helpers/storage';
+import {BASE_PATH} from 'coral-framework/constants/url';
+
+/**
+ * getAuthToken returns the active auth token or null
+ * Note: this method does not have access to the cookie based token used by
+ * browsers that don't allow us to use cross domain iframe local storage.
+ * @return {string|null}
+ */
+const getAuthToken = (store) => {
+ let state = store.getState();
+
+ if (state.config.auth_token) {
+
+ // if an auth_token exists in config, use it.
+ return state.config.auth_token;
+
+ } else if (!bowser.safari && !bowser.ios) {
+
+ // Use local storage auth tokens where there's a stable api.
+ return Storage.getItem('token');
+ }
+
+ return null;
+};
+
+export function createContext(reducers, plugins) {
+ const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
+ const eventEmitter = new EventEmitter({wildcard: true});
+ let store = null;
+ const token = () => {
+
+ // Try to get the token from localStorage. If it isn't here, it may
+ // be passed as a cookie.
+
+ // NOTE: THIS IS ONLY EVER EVALUATED ONCE, IN ORDER TO SEND A DIFFERNT
+ // TOKEN YOU MUST DISCONNECT AND RECONNECT THE WEBSOCKET CLIENT.
+ return getAuthToken(store);
+ };
+ const rest = createRestClient({
+ uri: `${BASE_PATH}api/v1`,
+ token,
+ });
+ const client = createClient({
+ uri: `${BASE_PATH}api/v1/graph/ql`,
+ liveUri: `${protocol}://${location.host}${BASE_PATH}api/v1/live`,
+ token,
+ });
+ const context = {
+ client,
+ pym,
+ plugins,
+ eventEmitter,
+ rest,
+ };
+
+ // Load plugin translations.
+ getTranslations(plugins).forEach((t) => loadTranslations(t));
+
+ // Pass any events through our parent.
+ eventEmitter.onAny((eventName, value) => {
+ pym.sendMessage('event', JSON.stringify({eventName, value}));
+ });
+
+ const finalReducers = {
+ ...reducers,
+ ...getReducers(plugins),
+ apollo: client.reducer(),
+ };
+
+ store = createStore(finalReducers, [
+ client.middleware(),
+ thunk.withExtraArgument(context),
+ apolloErrorReporter,
+ createReduxEmitter(eventEmitter),
+ ]);
+
+ return {
+ ...context,
+ store,
+ };
+}
diff --git a/client/coral-framework/services/client.js b/client/coral-framework/services/client.js
index 6f044f621..a354d68bc 100644
--- a/client/coral-framework/services/client.js
+++ b/client/coral-framework/services/client.js
@@ -1,64 +1,55 @@
-import ApolloClient, {addTypename, IntrospectionFragmentMatcher} from 'apollo-client';
-import {networkInterface} from './transport';
+import ApolloClient, {addTypename, IntrospectionFragmentMatcher, createNetworkInterface} from 'apollo-client';
import {SubscriptionClient, addGraphQLSubscriptions} from 'subscriptions-transport-ws';
import MessageTypes from 'subscriptions-transport-ws/dist/message-types';
-import {getAuthToken} from '../helpers/request';
import introspectionQueryResultData from '../graphql/introspection.json';
-import {BASE_PATH} from 'coral-framework/constants/url';
-let client, wsClient = null, wsClientToken = null;
-
-export function resetWebsocket() {
- if (wsClient === null) {
-
- // Nothing to reset!
- return;
+// Redux middleware to report any errors to the console.
+export const apolloErrorReporter = () => (next) => (action) => {
+ if (action.type === 'APOLLO_QUERY_ERROR') {
+ console.error(action.error);
}
+ return next(action);
+};
- // Close socket connection which will also unregister subscriptions on the server-side.
- wsClient.close();
-
- // Reconnect to the server.
- wsClient.connect();
-
- // Reregister all subscriptions (uses non public api).
- // See: https://github.com/apollographql/subscriptions-transport-ws/issues/171
- Object.keys(wsClient.operations).forEach((id) => {
- wsClient.sendMessage(id, MessageTypes.GQL_START, wsClient.operations[id].options);
- });
-}
-
-export function getClient(options = {}) {
- if (client) {
- return client;
- }
-
- const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
- wsClient = new SubscriptionClient(`${protocol}://${location.host}${BASE_PATH}api/v1/live`, {
+export function createClient(options = {}) {
+ const {token, uri, liveUri, ...apolloOptions} = options;
+ const wsClient = new SubscriptionClient(liveUri, {
reconnect: true,
lazy: true,
connectionParams: {
- get token() {
-
- wsClientToken = getAuthToken();
-
- // Try to get the token from localStorage. If it isn't here, it may
- // be passed as a cookie.
-
- // NOTE: THIS IS ONLY EVER EVALUATED ONCE, IN ORDER TO SEND A DIFFERNT
- // TOKEN YOU MUST DISCONNECT AND RECONNECT THE WEBSOCKET CLIENT.
- return wsClientToken;
- }
+ token,
}
});
+ const networkInterface = createNetworkInterface({
+ uri,
+ opts: {
+ credentials: 'same-origin'
+ }
+ });
+
+ networkInterface.use([{
+ applyMiddleware(req, next) {
+ if (!req.options.headers) {
+ req.options.headers = {}; // Create the header object if needed.
+ }
+
+ let authToken = typeof token === 'function' ? token() : token;
+ if (authToken) {
+ req.options.headers['authorization'] = `Bearer ${authToken}`;
+ }
+
+ next();
+ }
+ }]);
+
const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
networkInterface,
wsClient,
);
- client = new ApolloClient({
- ...options,
+ const client = new ApolloClient({
+ ...apolloOptions,
connectToDevTools: true,
addTypename: true,
fragmentMatcher: new IntrospectionFragmentMatcher({introspectionQueryResultData}),
@@ -72,5 +63,25 @@ export function getClient(options = {}) {
networkInterface: networkInterfaceWithSubscriptions,
});
+ client.resetWebsocket = () => {
+ if (wsClient === null) {
+
+ // Nothing to reset!
+ return;
+ }
+
+ // Close socket connection which will also unregister subscriptions on the server-side.
+ wsClient.close();
+
+ // Reconnect to the server.
+ wsClient.connect();
+
+ // Reregister all subscriptions (uses non public api).
+ // See: https://github.com/apollographql/subscriptions-transport-ws/issues/171
+ Object.keys(wsClient.operations).forEach((id) => {
+ wsClient.sendMessage(id, MessageTypes.GQL_START, wsClient.operations[id].options);
+ });
+ };
+
return client;
}
diff --git a/client/coral-framework/services/events.js b/client/coral-framework/services/events.js
index a1a2f4448..f6a62763d 100644
--- a/client/coral-framework/services/events.js
+++ b/client/coral-framework/services/events.js
@@ -1,5 +1,5 @@
export function createReduxEmitter(eventEmitter) {
- return (action) => {
+ return () => (next) => (action) => {
// Handle apollo actions.
if (action.type.startsWith('APOLLO_')) {
@@ -7,8 +7,10 @@ export function createReduxEmitter(eventEmitter) {
const {operationName, variables, result: {data}} = action;
eventEmitter.emit(`subscription.${operationName}.data`, {variables, data});
}
- return;
+ return next(action);
}
eventEmitter.emit(`action.${action.type}`, {action});
+
+ return next(action);
};
}
diff --git a/client/coral-framework/services/rest.js b/client/coral-framework/services/rest.js
new file mode 100644
index 000000000..f879659ca
--- /dev/null
+++ b/client/coral-framework/services/rest.js
@@ -0,0 +1,62 @@
+import merge from 'lodash/merge';
+
+const buildOptions = (inputOptions = {}) => {
+ const defaultOptions = {
+ method: 'GET',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ credentials: 'same-origin'
+ };
+
+ const options = merge({}, defaultOptions, inputOptions);
+ if (options.method.toLowerCase() !== 'get') {
+ options.body = JSON.stringify(options.body);
+ }
+
+ return options;
+};
+
+const handleResp = (res) => {
+ if (res.status > 399) {
+ return res.json().then((err) => {
+ let message = err.message || res.status;
+ const error = new Error();
+
+ if (err.error && err.error.metadata && err.error.metadata.message) {
+ error.metadata = err.error.metadata.message;
+ }
+
+ if (err.error && err.error.translation_key) {
+ error.translation_key = err.error.translation_key;
+ }
+
+ error.message = message;
+ error.status = res.status;
+ throw error;
+ });
+ } else if (res.status === 204) {
+ return res.text();
+ } else {
+ return res.json();
+ }
+};
+
+export function createRestClient(options) {
+ const {token, uri} = options;
+ const client = (path, options) => {
+ const authToken = typeof token === 'function' ? token() : token;
+ let opts = options;
+ if (authToken) {
+ opts = merge({}, options, {
+ headers: {
+ Authorization: `Bearer ${authToken}`,
+ },
+ });
+ }
+ return fetch(`${uri}${path}`, buildOptions(opts)).then(handleResp);
+ };
+ client.uri = uri;
+ return client;
+}
diff --git a/client/coral-framework/services/store.js b/client/coral-framework/services/store.js
index ec2479d15..e5fc52503 100644
--- a/client/coral-framework/services/store.js
+++ b/client/coral-framework/services/store.js
@@ -1,66 +1,21 @@
-import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
-import thunk from 'redux-thunk';
-import {getClient} from './client';
+import {createStore as reduxCreateStore, combineReducers, applyMiddleware, compose} from 'redux';
-let listeners = [];
-
-export function injectReducers(reducers) {
- const store = getStore();
- store.coralReducers = {...store.coralReducers, ...reducers};
- store.replaceReducer(combineReducers(store.coralReducers));
-}
-
-/**
- * Add a action listener to the redux store.
- * The action is passed as the first argument to the callback.
- */
-export function addListener(cb) {
- listeners.push(cb);
-}
-
-export function getStore() {
- if (window.coralStore) {
- return window.coralStore;
- }
-
- const apolloErrorReporter = () => (next) => (action) => {
- if (action.type === 'APOLLO_QUERY_ERROR') {
- console.error(action.error);
- }
- return next(action);
- };
-
- const customListener = () => (next) => (action) => {
- listeners.forEach((cb) => {cb(action);});
- return next(action);
- };
-
- const middlewares = [
+export function createStore(reducers, middlewares = []) {
+ const enhancers = [
applyMiddleware(
- getClient().middleware(),
- thunk,
- apolloErrorReporter,
- customListener,
+ ...middlewares,
),
];
if (window.devToolsExtension) {
// we can't have the last argument of compose() be undefined
- middlewares.push(window.devToolsExtension());
+ enhancers.push(window.devToolsExtension());
}
- const coralReducers = {
- apollo: getClient().reducer()
- };
-
- window.coralStore = createStore(
- combineReducers(coralReducers),
+ return reduxCreateStore(
+ combineReducers(reducers),
{},
- compose(...middlewares)
+ compose(...enhancers)
);
-
- window.coralStore.coralReducers = coralReducers;
-
- return window.coralStore;
}
diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js
deleted file mode 100644
index 060bfc34b..000000000
--- a/client/coral-framework/services/transport.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {createNetworkInterface} from 'apollo-client';
-import {getAuthToken} from '../helpers/request';
-import {BASE_PATH} from 'coral-framework/constants/url';
-
-//==============================================================================
-// NETWORK INTERFACE
-//==============================================================================
-
-const networkInterface = createNetworkInterface({
- uri: `${BASE_PATH}api/v1/graph/ql`,
- opts: {
- credentials: 'same-origin'
- }
-});
-
-//==============================================================================
-// MIDDLEWARES
-//==============================================================================
-
-networkInterface.use([{
- applyMiddleware(req, next) {
- if (!req.options.headers) {
- req.options.headers = {}; // Create the header object if needed.
- }
-
- let authToken = getAuthToken();
- if (authToken) {
- req.options.headers['authorization'] = `Bearer ${authToken}`;
- }
-
- next();
- }
-}]);
-
-export {
- networkInterface
-};