diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js
index bebe6a19b..9721123fa 100644
--- a/client/coral-admin/src/AppRouter.js
+++ b/client/coral-admin/src/AppRouter.js
@@ -1,13 +1,13 @@
import React from 'react';
import {Router, Route, IndexRoute, browserHistory} from 'react-router';
-import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
-import CommentStream from 'containers/CommentStream/CommentStream';
-import Configure from 'containers/Configure/Configure';
import Streams from 'containers/Streams/Streams';
-import CommunityContainer from 'containers/Community/CommunityContainer';
+import Configure from 'containers/Configure/Configure';
import LayoutContainer from 'containers/LayoutContainer';
+import CommentStream from 'containers/CommentStream/CommentStream';
import InstallContainer from 'containers/Install/InstallContainer';
+import CommunityContainer from 'containers/Community/CommunityContainer';
+import ModerationContainer from 'containers/ModerationQueue/ModerationContainer';
const routes = (
diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js
index 84d1b84ad..306461dd0 100644
--- a/client/coral-admin/src/index.js
+++ b/client/coral-admin/src/index.js
@@ -1,6 +1,15 @@
import React from 'react';
-import ReactDOM from 'react-dom';
+import {render} from 'react-dom';
+import {ApolloProvider} from 'react-apollo';
+
+import {client} from './services/client';
+import store from './services/store';
+
import App from './components/App';
-// Render the application into the DOM
-ReactDOM.render(
, document.querySelector('#root'));
+render(
+
+
+
+ , document.querySelector('#root')
+);
diff --git a/client/coral-admin/src/reducers/index.js b/client/coral-admin/src/reducers/index.js
index 1943e5976..f12de96ba 100644
--- a/client/coral-admin/src/reducers/index.js
+++ b/client/coral-admin/src/reducers/index.js
@@ -1,15 +1,13 @@
-import {combineReducers} from 'redux';
+import auth from 'reducers/auth';
+import users from 'reducers/users';
+import assets from 'reducers/assets';
+import actions from 'reducers/actions';
+import install from 'reducers/install';
import comments from 'reducers/comments';
import settings from 'reducers/settings';
import community from 'reducers/community';
-import users from 'reducers/users';
-import auth from 'reducers/auth';
-import actions from 'reducers/actions';
-import assets from 'reducers/assets';
-import install from 'reducers/install';
-// Combine all reducers into a main one
-export default combineReducers({
+export default {
settings,
comments,
community,
@@ -18,4 +16,4 @@ export default combineReducers({
assets,
users,
install
-});
+};
diff --git a/client/coral-framework/client.js b/client/coral-admin/src/services/client.js
similarity index 100%
rename from client/coral-framework/client.js
rename to client/coral-admin/src/services/client.js
diff --git a/client/coral-admin/src/services/store.js b/client/coral-admin/src/services/store.js
index 0700dc579..bf2735e45 100644
--- a/client/coral-admin/src/services/store.js
+++ b/client/coral-admin/src/services/store.js
@@ -1,17 +1,24 @@
-
-import {createStore, applyMiddleware} from 'redux';
+import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
-import mainReducer from 'reducers';
+import mainReducer from '../reducers';
+import {client} from './client';
-/**
- * Create the store by merging the app reducers with
- * the talk adapter. The talk adapter is the wire between
- * this client and the coral backend. The idea is we can
- * write different adapters for other platforms if we want
- */
+const middlewares = [
+ applyMiddleware(client.middleware()),
+ applyMiddleware(thunk)
+];
+
+if (window.devToolsExtension) {
+
+ // we can't have the last argument of compose() be undefined
+ middlewares.push(window.devToolsExtension());
+}
export default createStore(
- mainReducer,
- window.devToolsExtension && window.devToolsExtension(),
- applyMiddleware(thunk)
+ combineReducers({
+ ...mainReducer,
+ apollo: client.reducer()
+ }),
+ {},
+ compose(...middlewares)
);
diff --git a/client/coral-framework/subscriptions.js b/client/coral-admin/src/services/subscriptions.js
similarity index 100%
rename from client/coral-framework/subscriptions.js
rename to client/coral-admin/src/services/subscriptions.js
diff --git a/client/coral-framework/transport.js b/client/coral-admin/src/services/transport.js
similarity index 100%
rename from client/coral-framework/transport.js
rename to client/coral-admin/src/services/transport.js
diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js
index f75146f0a..3dc69400c 100644
--- a/client/coral-embed-stream/src/index.js
+++ b/client/coral-embed-stream/src/index.js
@@ -2,8 +2,8 @@ import React from 'react';
import {render} from 'react-dom';
import {ApolloProvider} from 'react-apollo';
-import {client} from 'coral-framework/client';
-import store from 'coral-framework/store';
+import {client} from 'coral-framework/services/client';
+import store from 'coral-framework/services/store';
import Embed from './Embed';
diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js
index d2cd4197b..56a2e522c 100644
--- a/client/coral-framework/index.js
+++ b/client/coral-framework/index.js
@@ -1,5 +1,5 @@
-import store from './store';
-import pym from './PymConnection';
+import store from './services/store';
+import pym from './services/PymConnection';
import I18n from './modules/i18n/i18n';
import * as authActions from './actions/auth';
import * as assetActions from './actions/asset';
diff --git a/client/coral-framework/PymConnection.js b/client/coral-framework/services/PymConnection.js
similarity index 77%
rename from client/coral-framework/PymConnection.js
rename to client/coral-framework/services/PymConnection.js
index ca592b824..6c492f2b8 100644
--- a/client/coral-framework/PymConnection.js
+++ b/client/coral-framework/services/PymConnection.js
@@ -1,4 +1,4 @@
-import Pym from '../../node_modules/pym.js';
+import Pym from '../../../node_modules/pym.js';
const pym = new Pym.Child({polling: 100});
export default pym;
diff --git a/client/coral-framework/services/client.js b/client/coral-framework/services/client.js
new file mode 100644
index 000000000..b4a7a38df
--- /dev/null
+++ b/client/coral-framework/services/client.js
@@ -0,0 +1,14 @@
+import ApolloClient, {addTypename} from 'apollo-client';
+import getNetworkInterface from './transport';
+
+export const client = new ApolloClient({
+ connectToDevTools: true,
+ queryTransformer: addTypename,
+ dataIdFromObject: (result) => {
+ if (result.id && result.__typename) { // eslint-disable-line no-underscore-dangle
+ return result.__typename + result.id; // eslint-disable-line no-underscore-dangle
+ }
+ return null;
+ },
+ networkInterface: getNetworkInterface()
+});
diff --git a/client/coral-framework/store.js b/client/coral-framework/services/store.js
similarity index 93%
rename from client/coral-framework/store.js
rename to client/coral-framework/services/store.js
index 439569ee1..bf2735e45 100644
--- a/client/coral-framework/store.js
+++ b/client/coral-framework/services/store.js
@@ -1,6 +1,6 @@
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
-import mainReducer from './reducers';
+import mainReducer from '../reducers';
import {client} from './client';
const middlewares = [
diff --git a/client/coral-framework/services/subscriptions.js b/client/coral-framework/services/subscriptions.js
new file mode 100644
index 000000000..818a6fb33
--- /dev/null
+++ b/client/coral-framework/services/subscriptions.js
@@ -0,0 +1,16 @@
+import {print} from 'graphql-tag/printer';
+
+// quick way to add the subscribe and unsubscribe functions to the network interface
+const addGraphQLSubscriptions = (networkInterface, wsClient) => {
+ return Object.assign(networkInterface, {
+ subscribe: (request, handler) => wsClient.subscribe({
+ query: print(request.query),
+ variables: request.variables,
+ }, handler),
+ unsubscribe: (id) => {
+ wsClient.unsubscribe(id);
+ },
+ });
+};
+
+export default addGraphQLSubscriptions;
diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js
new file mode 100644
index 000000000..2bd6ac636
--- /dev/null
+++ b/client/coral-framework/services/transport.js
@@ -0,0 +1,11 @@
+import {createNetworkInterface} from 'apollo-client';
+
+export default function getNetworkInterface(apiUrl = '/api/v1/graph/ql', headers = {}) {
+ return new createNetworkInterface({
+ uri: apiUrl,
+ opts: {
+ credentials: 'same-origin',
+ headers,
+ },
+ });
+}
diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js
index 4398ed5c6..9e72cdadc 100644
--- a/client/coral-settings/containers/SettingsContainer.js
+++ b/client/coral-settings/containers/SettingsContainer.js
@@ -5,7 +5,7 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import {myCommentHistory} from 'coral-framework/graphql/queries';
-import {link} from 'coral-framework/PymConnection';
+import {link} from 'coral-framework/services/PymConnection';
import NotLoggedIn from '../components/NotLoggedIn';
import {Spinner} from 'coral-ui';
import SettingsHeader from '../components/SettingsHeader';
diff --git a/views/article.ejs b/views/article.ejs
index 1315acac6..3869262ff 100644
--- a/views/article.ejs
+++ b/views/article.ejs
@@ -27,7 +27,7 @@