diff --git a/client/coral-admin/src/components/LoadMore.css b/client/coral-admin/src/components/LoadMore.css
new file mode 100644
index 000000000..64c51f870
--- /dev/null
+++ b/client/coral-admin/src/components/LoadMore.css
@@ -0,0 +1,21 @@
+.loadMoreContainer {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+}
+
+.loadMore {
+ width: 100%;
+ text-align: center;
+ color: #FFF;
+ max-width: 660px;
+ margin-bottom: 30px;
+ background-color: #2376D8;
+ cursor: pointer;
+}
+
+.loadMore:hover {
+ background-color: #4399FF;
+}
+
+
diff --git a/client/coral-admin/src/routes/Moderation/components/LoadMore.js b/client/coral-admin/src/components/LoadMore.js
similarity index 92%
rename from client/coral-admin/src/routes/Moderation/components/LoadMore.js
rename to client/coral-admin/src/components/LoadMore.js
index 612629647..ac7ea38d6 100644
--- a/client/coral-admin/src/routes/Moderation/components/LoadMore.js
+++ b/client/coral-admin/src/components/LoadMore.js
@@ -1,6 +1,6 @@
import React, {PropTypes} from 'react';
import {Button} from 'coral-ui';
-import styles from './styles.css';
+import styles from './LoadMore.css';
const LoadMore = ({loadMore, showLoadMore}) =>
diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js
index eab25c41d..dcc40a029 100644
--- a/client/coral-admin/src/components/UserDetail.js
+++ b/client/coral-admin/src/components/UserDetail.js
@@ -6,6 +6,7 @@ import {Slot} from 'coral-framework/components';
import ButtonCopyToClipboard from './ButtonCopyToClipboard';
import {actionsMap} from '../utils/moderationQueueActionsMap';
import ClickOutside from 'coral-framework/components/ClickOutside';
+import LoadMore from '../components/LoadMore';
export default class UserDetail extends React.Component {
@@ -59,7 +60,7 @@ export default class UserDetail extends React.Component {
user,
totalComments,
rejectedComments,
- comments: {nodes}
+ comments: {nodes, hasNextPage}
},
activeTab,
selectedCommentIds,
@@ -70,6 +71,7 @@ export default class UserDetail extends React.Component {
bulkReject,
hideUserDetail,
viewUserDetail,
+ loadMore,
} = this.props;
const localProfile = user.profiles.find((p) => p.provider === 'local');
@@ -167,6 +169,10 @@ export default class UserDetail extends React.Component {
})
}
+
);
diff --git a/client/coral-admin/src/components/UserDetailComment.css b/client/coral-admin/src/components/UserDetailComment.css
index 0e10f00c0..b042aea39 100644
--- a/client/coral-admin/src/components/UserDetailComment.css
+++ b/client/coral-admin/src/components/UserDetailComment.css
@@ -8,6 +8,10 @@
min-height: 0;
}
+.root:last-child {
+ border: 0;
+}
+
.rootSelected {
background-color: #ecf4ff;
}
diff --git a/client/coral-admin/src/containers/UserDetail.js b/client/coral-admin/src/containers/UserDetail.js
index a54e70734..e59629a85 100644
--- a/client/coral-admin/src/containers/UserDetail.js
+++ b/client/coral-admin/src/containers/UserDetail.js
@@ -14,6 +14,7 @@ import {
} from 'coral-admin/src/actions/userDetail';
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
import UserDetailComment from './UserDetailComment';
+import update from 'immutability-helper';
const commentConnectionFragment = gql`
fragment CoralAdmin_Moderation_CommentConnection on CommentConnection {
@@ -32,6 +33,7 @@ const slots = [
];
class UserDetailContainer extends React.Component {
+ isLoadingMore = false;
// status can be 'ACCEPTED' or 'REJECTED'
bulkSetCommentStatus = (status) => {
@@ -40,7 +42,6 @@ class UserDetailContainer extends React.Component {
});
Promise.all(changes).then(() => {
- this.props.data.refetch(); // some comments may have moved out of this tab
this.props.clearUserDetailSelections(); // un-select everything
});
}
@@ -61,12 +62,53 @@ class UserDetailContainer extends React.Component {
return this.props.setCommentStatus({commentId, status: 'REJECTED'});
}
+ loadMore = () => {
+ if (this.isLoadingMore) {
+ return;
+ }
+
+ this.isLoadingMore = true;
+ const variables = {
+ limit: 10,
+ cursor: this.props.root.comments.endCursor,
+ author_id: this.props.data.variables.author_id,
+ statuses: this.props.data.variables.statuses,
+ };
+ this.props.data.fetchMore({
+ query: LOAD_MORE_QUERY,
+ variables,
+ updateQuery: (prev, {fetchMoreResult:{comments}}) => {
+ return update(prev, {
+ comments: {
+ nodes: {$push: comments.nodes},
+ hasNextPage: {$set: comments.hasNextPage},
+ startCursor: {$set: comments.startCursor},
+ endCursor: {$set: comments.endCursor},
+ },
+ });
+ }
+ })
+ .then(() => {
+ this.isLoadingMore = false;
+ })
+ .catch((err) => {
+ this.isLoadingMore = false;
+ throw err;
+ });
+ };
+
+ componentWillReceiveProps(next) {
+ if (this.props.userId === null && next.userId) {
+ next.data.refetch();
+ }
+ }
+
render () {
if (!this.props.userId) {
return null;
}
- const loading = !('user' in this.props.root) || this.props.root.user.id !== this.props.userId;
+ const loading = [1, 2, 4].indexOf(this.props.data.networkStatus) >= 0;
return ;
}
}
+const LOAD_MORE_QUERY = gql`
+ query CoralAdmin_Moderation_LoadMore($limit: Int = 10, $cursor: Date, $author_id: ID!, $statuses: [COMMENT_STATUS!]) {
+ comments(query: {limit: $limit, cursor: $cursor, author_id: $author_id, statuses: $statuses}) {
+ ...CoralAdmin_Moderation_CommentConnection
+ }
+ }
+ ${commentConnectionFragment}
+`;
+
export const withUserDetailQuery = withQuery(gql`
query CoralAdmin_UserDetail($author_id: ID!, $statuses: [COMMENT_STATUS!]) {
user(id: $author_id) {
diff --git a/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js b/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js
index 57b551df2..7f96c7c90 100644
--- a/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js
+++ b/client/coral-admin/src/routes/Moderation/components/ModerationQueue.js
@@ -4,7 +4,7 @@ import Comment from '../containers/Comment';
import styles from './styles.css';
import EmptyCard from '../../../components/EmptyCard';
import {actionsMap} from '../../../utils/moderationQueueActionsMap';
-import LoadMore from './LoadMore';
+import LoadMore from '../../../components/LoadMore';
import t from 'coral-framework/services/i18n';
import {CSSTransitionGroup} from 'react-transition-group';
diff --git a/client/coral-admin/src/routes/Moderation/components/styles.css b/client/coral-admin/src/routes/Moderation/components/styles.css
index ccc762641..72c5e4d63 100644
--- a/client/coral-admin/src/routes/Moderation/components/styles.css
+++ b/client/coral-admin/src/routes/Moderation/components/styles.css
@@ -397,26 +397,6 @@ span {
}
}
-.loadMoreContainer {
- display: flex;
- justify-content: center;
- width: 100%;
-};
-
-.loadMore {
- width: 100%;
- text-align: center;
- color: #FFF;
- max-width: 660px;
- margin-bottom: 30px;
- background-color: #2376D8;
- cursor: pointer;
-}
-
-.loadMore:hover {
- background-color: #4399FF;
-}
-
.tabIcon {
position: relative;
top: 3px;
@@ -499,4 +479,4 @@ span {
right: 0px;
top: 0px;
text-align: right;
-}
\ No newline at end of file
+}
diff --git a/graph/helpers/response.js b/graph/helpers/response.js
index ebdbc02ec..f5e7a54f4 100644
--- a/graph/helpers/response.js
+++ b/graph/helpers/response.js
@@ -2,18 +2,19 @@ const errors = require('../../errors');
const {Error: {ValidationError}} = require('mongoose');
/**
- * Wraps up a promise to return an object with the resolution of the promise
+ * Wraps up a promise or value to return an object with the resolution of the promise
* keyed at `key` or an error caught at `errors`.
*/
-const wrapResponse = (key) => (promise) => {
- return promise.then((value) => {
+const wrapResponse = (key) => (promiseOrValue) => {
+ return Promise.resolve(promiseOrValue).then((value) => {
let res = {};
if (key) {
res[key] = value;
}
return res;
- }).catch((err) => {
+ })
+ .catch((err) => {
if (err instanceof errors.APIError) {
return {
errors: [err]