diff --git a/app/components/component-dashboard/src/components/sections/EditorItem.js b/app/components/component-dashboard/src/components/sections/EditorItem.js
index 8dfbb1e03f3eb7d23c3d220c6b576cb26f7760eb..54237a55cdc37d9936902f0bbbc4ed7839f90b8d 100644
--- a/app/components/component-dashboard/src/components/sections/EditorItem.js
+++ b/app/components/component-dashboard/src/components/sections/EditorItem.js
@@ -45,7 +45,14 @@ const EditorItemLinks = ({ version }) => (
 )
 
 EditorItemLinks.propTypes = {
-  version: PropTypes.element.isRequired,
+  version: PropTypes.shape({
+    id: PropTypes.string.isRequired,
+    parentId: PropTypes.string,
+    decision: PropTypes.shape({
+      status: PropTypes.string.isRequired,
+      recommendation: PropTypes.string.isRequired,
+    }),
+  }).isRequired,
 }
 
 const getDeclarationsObject = (version, value) => {
@@ -102,7 +109,31 @@ const EditorItem = ({ version }) => (
 )
 
 EditorItem.propTypes = {
-  version: PropTypes.element.isRequired,
+  version: PropTypes.shape({
+    id: PropTypes.string.isRequired,
+    parentId: PropTypes.string,
+    decision: PropTypes.shape({
+      status: PropTypes.string.isRequired,
+      recommendation: PropTypes.string.isRequired,
+    }),
+    meta: PropTypes.shape({
+      title: PropTypes.string.isRequired,
+    }).isRequired,
+    // eslint-disable-next-line react/forbid-prop-types
+    published: PropTypes.any, // TODO require boolean rather than any truthy or falsey value
+    status: PropTypes.string.isRequired,
+    teams: PropTypes.arrayOf(
+      PropTypes.shape({
+        role: PropTypes.string.isRequired,
+        members: PropTypes.arrayOf(
+          PropTypes.shape({
+            status: PropTypes.string,
+          }).isRequired,
+        ).isRequired,
+      }).isRequired,
+    ).isRequired,
+    _currentRoles: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
+  }).isRequired,
 }
 
 export default EditorItem
diff --git a/app/components/component-dashboard/src/components/sections/ReviewerItem.js b/app/components/component-dashboard/src/components/sections/ReviewerItem.js
index 28b34d9ffa13aacc5b8f7c94494e558e195d6f0a..911a6dba70931a6e634f684790daa2fd9051057f 100644
--- a/app/components/component-dashboard/src/components/sections/ReviewerItem.js
+++ b/app/components/component-dashboard/src/components/sections/ReviewerItem.js
@@ -72,7 +72,24 @@ const ReviewerItem = ({ version, currentUser, reviewerRespond }) => {
 }
 
 ReviewerItem.propTypes = {
-  version: PropTypes.string.isRequired,
+  version: PropTypes.shape({
+    id: PropTypes.string.isRequired,
+    meta: PropTypes.shape({
+      title: PropTypes.string.isRequired,
+    }).isRequired,
+    teams: PropTypes.arrayOf(
+      PropTypes.shape({
+        role: PropTypes.string.isRequired,
+        members: PropTypes.arrayOf(
+          PropTypes.shape({
+            user: PropTypes.shape({
+              id: PropTypes.string.isRequired,
+            }).isRequired,
+          }).isRequired,
+        ).isRequired,
+      }).isRequired,
+    ).isRequired,
+  }).isRequired,
   currentUser: PropTypes.oneOfType([PropTypes.object]).isRequired,
   reviewerRespond: PropTypes.func.isRequired,
 }
diff --git a/app/components/component-manuscripts/src/Manuscript.jsx b/app/components/component-manuscripts/src/Manuscript.js
similarity index 78%
rename from app/components/component-manuscripts/src/Manuscript.jsx
rename to app/components/component-manuscripts/src/Manuscript.js
index 529e267e8ca0c5fc47a855c95e25e1f1a46a0b12..32fc549d6330340701bb1612fc1c084a61debc29 100644
--- a/app/components/component-manuscripts/src/Manuscript.jsx
+++ b/app/components/component-manuscripts/src/Manuscript.js
@@ -1,7 +1,5 @@
-/* eslint-disable react/jsx-filename-extension */
 import React from 'react'
-import gql from 'graphql-tag'
-import { useMutation } from '@apollo/client'
+import { gql, useMutation } from '@apollo/client'
 // import { Action } from '@pubsweet/ui'
 import config from 'config'
 import PropTypes from 'prop-types'
@@ -83,9 +81,22 @@ const User = ({ manuscriptId, manuscript, submitter }) => {
 }
 
 User.propTypes = {
-  manuscriptId: PropTypes.number.isRequired,
-  manuscript: PropTypes.element.isRequired,
-  submitter: PropTypes.element.isRequired,
+  manuscriptId: PropTypes.string.isRequired,
+  manuscript: PropTypes.shape({
+    meta: PropTypes.shape({
+      title: PropTypes.string.isRequired,
+    }).isRequired,
+    created: PropTypes.string.isRequired,
+    updated: PropTypes.string,
+    status: PropTypes.string.isRequired,
+  }).isRequired,
+  submitter: PropTypes.shape({
+    defaultIdentity: PropTypes.shape({
+      name: PropTypes.string.isRequired,
+    }).isRequired,
+    email: PropTypes.string,
+    username: PropTypes.string.isRequired,
+  }).isRequired,
 }
 
 export default User
diff --git a/app/components/component-review/src/components/decision/DecisionReviews.js b/app/components/component-review/src/components/decision/DecisionReviews.js
index c4a741dc07f36dd81c8a3155bc009c2ee4a22ae7..9b3b79b9af6ecf4a9818004cea4fc39bcec7038d 100644
--- a/app/components/component-review/src/components/decision/DecisionReviews.js
+++ b/app/components/component-review/src/components/decision/DecisionReviews.js
@@ -62,7 +62,28 @@ const DecisionReviews = ({ manuscript }) => (
 )
 
 DecisionReviews.propTypes = {
-  manuscript: PropTypes.element.isRequired,
+  manuscript: PropTypes.shape({
+    id: PropTypes.string.isRequired,
+    reviews: PropTypes.arrayOf(
+      PropTypes.shape({
+        user: PropTypes.shape({
+          id: PropTypes.string.isRequired,
+        }).isRequired,
+      }).isRequired,
+    ).isRequired,
+    teams: PropTypes.arrayOf(
+      PropTypes.shape({
+        role: PropTypes.string.isRequired,
+        members: PropTypes.arrayOf(
+          PropTypes.shape({
+            user: PropTypes.shape({
+              id: PropTypes.string.isRequired,
+            }).isRequired,
+          }).isRequired,
+        ).isRequired,
+      }).isRequired,
+    ).isRequired,
+  }).isRequired,
 }
 
 export default DecisionReviews
diff --git a/app/components/component-review/src/components/metadata/ReviewMetadata.js b/app/components/component-review/src/components/metadata/ReviewMetadata.js
index 934032b9196b5788743324d6c2ebcc35ddf372f5..b2b05b5585ffde391f334a61944341146ed22f06 100644
--- a/app/components/component-review/src/components/metadata/ReviewMetadata.js
+++ b/app/components/component-review/src/components/metadata/ReviewMetadata.js
@@ -130,7 +130,22 @@ const ReviewMetadata = ({ manuscript: rawManuscript }) => {
 }
 
 ReviewMetadata.propTypes = {
-  manuscript: PropTypes.node.isRequired,
+  manuscript: PropTypes.shape({
+    meta: PropTypes.shape({
+      notes: PropTypes.arrayOf(
+        PropTypes.shape({
+          notesType: PropTypes.string.isRequired,
+          content: PropTypes.string.isRequired,
+        }).isRequired,
+      ).isRequired,
+    }).isRequired,
+    files: PropTypes.arrayOf(
+      PropTypes.shape({
+        url: PropTypes.string.isRequired,
+        filename: PropTypes.string.isRequired,
+      }).isRequired,
+    ).isRequired,
+  }).isRequired,
 }
 
 export default ReviewMetadata
diff --git a/docker-compose.yml b/docker-compose.yml
index ba141465cf0f85cc35f11367efaabcd3b5124bf5..e0ab7f26119db5d0ba6a2aac857baf3fe71b3183 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
       - ${CLIENT_PORT:-4000}:${CLIENT_PORT:-4000}
     environment:
       - NODE_ENV=development
+      - CLIENT_PROTOCOL=${CLIENT_PROTOCOL:-http}
       - CLIENT_HOST=0.0.0.0
       - CLIENT_PORT=${CLIENT_PORT:-4000}
       - SERVER_PROTOCOL=http
diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js
index b577a01892e82b44f0dca8098d40cf050d556aab..3017b5044b898ad405bb7fc5272583b94a5112e4 100644
--- a/server/model-manuscript/src/graphql.js
+++ b/server/model-manuscript/src/graphql.js
@@ -148,12 +148,15 @@ const resolvers = {
       return id
     },
     async reviewerResponse(_, { action, teamId }, context) {
-      // eslint-disable-next-line global-require
-      const { TeamModel, ReviewModel } = require('@pubsweet/models') // Pubsweet models may initially be undefined, so we require only when resolver runs.
+      const {
+        Team: TeamModel,
+        Review: ReviewModel,
+        // eslint-disable-next-line global-require
+      } = require('@pubsweet/models') // Pubsweet models may initially be undefined, so we require only when resolver runs.
 
       if (action !== 'accepted' && action !== 'rejected')
         throw new Error(
-          `Invalid action (revieweResponse): Must be either "accepted" or "rejected"`,
+          `Invalid action (reviewerResponse): Must be either "accepted" or "rejected"`,
         )
 
       const team = await TeamModel.query().findById(teamId).eager('members')
diff --git a/server/subscriptions.js b/server/subscriptions.js
index f4f2de1fbfde074e091a91a262ae0d83c7251b7a..597e484d11ea3346ed62a03a033b3c0c1d1745f9 100644
--- a/server/subscriptions.js
+++ b/server/subscriptions.js
@@ -2,7 +2,8 @@
  * subscription (websocket) server for GraphQL
  */
 const { execute, subscribe } = require('graphql')
-const { SubscriptionServer } = require('subscriptions-transport-ws')
+// eslint-disable-next-line import/no-extraneous-dependencies
+const { SubscriptionServer } = require('subscriptions-transport-ws') // TODO: Should we rewrite subscriptions to use Apollo v2?
 
 const logger = require('@pubsweet/logger')
 
@@ -11,10 +12,12 @@ const { token } = require('pubsweet-server/src/authentication') // TODO: Fix imp
 
 module.exports = {
   addSubscriptions: server => {
+    /* eslint-disable global-require */
     const models = require('@pubsweet/models')
     const helpers = require('pubsweet-server/src/helpers/authorization')
-
     const { User } = require('@pubsweet/models')
+    /* eslint-enable global-require */
+
     SubscriptionServer.create(
       {
         schema: graphqlSchema,
@@ -22,8 +25,11 @@ module.exports = {
         subscribe,
         onConnect: async (connectionParams, webSocket, context) => {
           if (!connectionParams.authToken) {
-            throw new Error('Missing auth token')
+            logger.info('Missing auth token')
+            return false
+            // throw new Error('Missing auth token')
           }
+
           const addTocontext = await new Promise((resolve, reject) => {
             token.verify(connectionParams.authToken, (_, id) => {
               if (!id) {
@@ -34,6 +40,7 @@ module.exports = {
               resolve({ userId: id, models, helpers })
             })
           })
+
           // Record a user's online status
           const user = await User.query().updateAndFetchById(
             addTocontext.userId,
@@ -45,6 +52,7 @@ module.exports = {
         },
         onDisconnect: async (webSocket, context) => {
           const initialContext = await context.initPromise
+
           // Record that a user is no longer online
           if (initialContext.user && initialContext.user.id) {
             await User.query()