diff --git a/app.js b/app.js
index b65126aa0..c180d2c04 100644
--- a/app.js
+++ b/app.js
@@ -1,8 +1,10 @@
const express = require('express');
const morgan = require('morgan');
const path = require('path');
+const uuid = require('uuid');
const merge = require('lodash/merge');
const helmet = require('helmet');
+const plugins = require('./services/plugins');
const compression = require('compression');
const {HELMET_CONFIGURATION} = require('./config');
const {MOUNT_PATH} = require('./url');
@@ -11,6 +13,25 @@ const debug = require('debug')('talk:app');
const app = express();
+// Request Identity Middleware
+app.use((req, res, next) => {
+ req.id = uuid.v4();
+
+ next();
+});
+
+//==============================================================================
+// PLUGIN PRE APPLICATION MIDDLEWARE
+//==============================================================================
+
+// Inject server route plugins.
+plugins.get('server', 'app').forEach(({plugin, app: callback}) => {
+ debug(`added plugin '${plugin.name}'`);
+
+ // Pass the app to the plugin to mount it's routes.
+ callback(app);
+});
+
//==============================================================================
// APPLICATION WIDE MIDDLEWARE
//==============================================================================
diff --git a/client/coral-admin/src/routes/Moderation/graphql.js b/client/coral-admin/src/routes/Moderation/graphql.js
index 5758315eb..86bc524b8 100644
--- a/client/coral-admin/src/routes/Moderation/graphql.js
+++ b/client/coral-admin/src/routes/Moderation/graphql.js
@@ -152,6 +152,10 @@ export function cleanUpQueue(root, queue, sortOrder, queueConfig) {
let nodes = root[queue].nodes;
let hasNextPage = root[queue].hasNextPage;
+ if (!nodes.length) {
+ return root;
+ }
+
if (queueConfig) {
nodes = root[queue].nodes.filter((comment) => commentBelongToQueue(queue, comment, queueConfig));
}
@@ -161,8 +165,8 @@ export function cleanUpQueue(root, queue, sortOrder, queueConfig) {
sortOrder,
);
- if (nodes.length > 2) {
- nodes = nodes.slice(0, 2);
+ if (nodes.length > 100) {
+ nodes = nodes.slice(0, 100);
hasNextPage = true;
}
diff --git a/client/coral-ui/components/Dialog.css b/client/coral-ui/components/Dialog.css
new file mode 100644
index 000000000..47bc43918
--- /dev/null
+++ b/client/coral-ui/components/Dialog.css
@@ -0,0 +1,13 @@
+
+.dialog {
+}
+
+:global(.backdrop) {
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ background-color: black;
+ opacity: 0.1;
+}
diff --git a/client/coral-ui/components/Dialog.js b/client/coral-ui/components/Dialog.js
index a7d34a512..bba386026 100644
--- a/client/coral-ui/components/Dialog.js
+++ b/client/coral-ui/components/Dialog.js
@@ -2,6 +2,8 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import dialogPolyfill from 'dialog-polyfill';
import 'dialog-polyfill/dialog-polyfill.css';
+import styles from './Dialog.css';
+import {Portal} from 'react-portal';
export default class Dialog extends Component {
static propTypes = {
@@ -52,13 +54,15 @@ export default class Dialog extends Component {
const {children, className = '', onClose, onCancel, open, ...rest} = this.props; // eslint-disable-line
return (
-
+
+
+
);
}
}
diff --git a/graph/context.js b/graph/context.js
index d6d85a651..e894c1534 100644
--- a/graph/context.js
+++ b/graph/context.js
@@ -42,14 +42,15 @@ const decorateContextPlugins = (context, contextPlugins) => {
* Stores the request context.
*/
class Context {
- constructor({user = null}) {
+ constructor(parent) {
- // Generate a new context id for the request.
- this.id = uuid.v4();
+ // Generate a new context id for the request if the parent doesn't provide
+ // one.
+ this.id = parent.id || uuid.v4();
- // Load the current logged in user to `user`, otherwise this'll be null.
- if (user) {
- this.user = user;
+ // Load the current logged in user to `user`, otherwise this will be null.
+ if (parent.user) {
+ this.user = parent.user;
}
// Attach the connectors.
@@ -66,6 +67,9 @@ class Context {
// Bind the publish/subscribe to the context.
this.pubsub = pubsub.getClient();
+
+ // Bind the parent context.
+ this.parent = parent;
}
}
diff --git a/package.json b/package.json
index d2bae9089..10626267e 100644
--- a/package.json
+++ b/package.json
@@ -160,6 +160,7 @@
"react-mdl": "^1.7.2",
"react-mdl-selectfield": "^0.2.0",
"react-paginate": "^5.0.0",
+ "react-portal": "^4.1.2",
"react-recaptcha": "^2.2.6",
"react-redux": "^4.4.5",
"react-router": "^3.0.0",
diff --git a/plugins.js b/plugins.js
index dfe3d3312..487825be7 100644
--- a/plugins.js
+++ b/plugins.js
@@ -55,7 +55,8 @@ const hookSchemas = {
loaders: Joi.func().maxArity(1),
mutators: Joi.func().maxArity(1),
resolvers: Joi.object().pattern(/\w/, Joi.object().pattern(/(?:__resolveType|\w+)/, Joi.func())),
- typeDefs: Joi.string()
+ typeDefs: Joi.string(),
+ schemaLevelResolveFunction: Joi.func(),
};
/**
@@ -172,7 +173,7 @@ class PluginSection {
if (this.required) {
return;
}
-
+
this.required = true;
this.plugins.forEach((plugin) => {
diff --git a/test/e2e/page_objects/admin.js b/test/e2e/page_objects/admin.js
index aa3b58ab5..c76a45d9e 100644
--- a/test/e2e/page_objects/admin.js
+++ b/test/e2e/page_objects/admin.js
@@ -57,6 +57,10 @@ module.exports = {
suspendUserDialog: '.talk-admin-suspend-user-dialog',
suspendUserConfirmButton: '.talk-admin-suspend-user-dialog-confirm',
supendUserSendButton: '.talk-admin-suspend-user-dialog-send',
+ usernameDialog: '.talk-reject-username-dialog',
+ usernameDialogButtons: '.talk-reject-username-dialog-buttons',
+ usernameDialogSuspend: '.talk-reject-username-dialog-button-k',
+ usernameDialogSuspensionMessage: '.talk-reject-username-dialog-suspension-message',
toast: '.toastify',
toastClose: '.toastify__close',
},
@@ -97,10 +101,6 @@ module.exports = {
flaggedUser:'.talk-admin-community-flagged-user',
flaggedUserApproveButton: '.talk-admin-flagged-user-approve-button',
flaggedUserRejectButton: '.talk-admin-flagged-user-reject-button',
- usernameDialog: '.talk-reject-username-dialog',
- usernameDialogButtons: '.talk-reject-username-dialog-buttons',
- usernameDialogSuspend: '.talk-reject-username-dialog-button-k',
- usernameDialogSuspensionMessage: '.talk-reject-username-dialog-suspension-message'
},
sections: {
people: {
@@ -137,6 +137,10 @@ module.exports = {
this.parent
.click('@drawerOverlay')
.waitForElementNotPresent('@drawerOverlay');
+
+ // Wait a bit to let animations terminate cleanly.
+ this.api.pause(200);
+
return this.parent;
},
}],
diff --git a/test/e2e/specs/04_userStatus.js b/test/e2e/specs/04_userStatus.js
index 41540db65..011f59056 100644
--- a/test/e2e/specs/04_userStatus.js
+++ b/test/e2e/specs/04_userStatus.js
@@ -64,15 +64,18 @@ module.exports = {
.click('@flaggedUserRejectButton');
},
'admin suspends the user': (client) => {
+ const adminPage = client.page.admin();
const community = client.page.admin().section.community;
- community
+ adminPage
.waitForElementVisible('@usernameDialog')
.waitForElementVisible('@usernameDialogButtons')
.waitForElementVisible('@usernameDialogSuspend')
.click('@usernameDialogSuspend')
.waitForElementVisible('@usernameDialogSuspensionMessage')
- .click('@usernameDialogSuspend')
+ .click('@usernameDialogSuspend');
+
+ community
.waitForElementNotPresent('@flaggedUser');
},
'admin logs out': (client) => {
diff --git a/yarn.lock b/yarn.lock
index 282e501e1..c380bcd3a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7473,6 +7473,12 @@ react-paginate@^5.0.0:
prop-types "^15.6.0"
react-addons-create-fragment "^15.0.0"
+react-portal@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.1.2.tgz#7f28f3c8c2ed5c541907c0ed0f24e8996acf627f"
+ dependencies:
+ prop-types "^15.5.8"
+
react-recaptcha@^2.2.6:
version "2.3.5"
resolved "https://registry.yarnpkg.com/react-recaptcha/-/react-recaptcha-2.3.5.tgz#a5db337125bb00fb13c2fa2e4ebfbe8b0cd06bb7"