mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
Merge branch 'master' into update-my-comments
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+4
-3
@@ -1,9 +1,10 @@
|
|||||||
import React, {PropTypes} from 'react';
|
import React, {PropTypes} from 'react';
|
||||||
import {Button} from 'coral-ui';
|
import {Button} from 'coral-ui';
|
||||||
import styles from './styles.css';
|
import styles from './LoadMore.css';
|
||||||
|
import cn from 'classnames';
|
||||||
|
|
||||||
const LoadMore = ({loadMore, showLoadMore}) =>
|
const LoadMore = ({loadMore, showLoadMore, className, ...rest}) =>
|
||||||
<div className={styles.loadMoreContainer}>
|
<div {...rest} className={cn(className, styles.loadMoreContainer)}>
|
||||||
{
|
{
|
||||||
showLoadMore && <Button
|
showLoadMore && <Button
|
||||||
className={styles.loadMore}
|
className={styles.loadMore}
|
||||||
@@ -79,3 +79,12 @@
|
|||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadMore > button {
|
||||||
|
background-color: #696969;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #404040;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {Slot} from 'coral-framework/components';
|
|||||||
import ButtonCopyToClipboard from './ButtonCopyToClipboard';
|
import ButtonCopyToClipboard from './ButtonCopyToClipboard';
|
||||||
import {actionsMap} from '../utils/moderationQueueActionsMap';
|
import {actionsMap} from '../utils/moderationQueueActionsMap';
|
||||||
import ClickOutside from 'coral-framework/components/ClickOutside';
|
import ClickOutside from 'coral-framework/components/ClickOutside';
|
||||||
|
import LoadMore from '../components/LoadMore';
|
||||||
|
|
||||||
export default class UserDetail extends React.Component {
|
export default class UserDetail extends React.Component {
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ export default class UserDetail extends React.Component {
|
|||||||
user,
|
user,
|
||||||
totalComments,
|
totalComments,
|
||||||
rejectedComments,
|
rejectedComments,
|
||||||
comments: {nodes}
|
comments: {nodes, hasNextPage}
|
||||||
},
|
},
|
||||||
activeTab,
|
activeTab,
|
||||||
selectedCommentIds,
|
selectedCommentIds,
|
||||||
@@ -70,6 +71,7 @@ export default class UserDetail extends React.Component {
|
|||||||
bulkReject,
|
bulkReject,
|
||||||
hideUserDetail,
|
hideUserDetail,
|
||||||
viewUserDetail,
|
viewUserDetail,
|
||||||
|
loadMore,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const localProfile = user.profiles.find((p) => p.provider === 'local');
|
const localProfile = user.profiles.find((p) => p.provider === 'local');
|
||||||
@@ -167,6 +169,11 @@ export default class UserDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<LoadMore
|
||||||
|
className={styles.loadMore}
|
||||||
|
loadMore={loadMore}
|
||||||
|
showLoadMore={hasNextPage}
|
||||||
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</ClickOutside>
|
</ClickOutside>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.rootSelected {
|
.rootSelected {
|
||||||
background-color: #ecf4ff;
|
background-color: #ecf4ff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from 'coral-admin/src/actions/userDetail';
|
} from 'coral-admin/src/actions/userDetail';
|
||||||
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
|
||||||
import UserDetailComment from './UserDetailComment';
|
import UserDetailComment from './UserDetailComment';
|
||||||
|
import update from 'immutability-helper';
|
||||||
|
|
||||||
const commentConnectionFragment = gql`
|
const commentConnectionFragment = gql`
|
||||||
fragment CoralAdmin_Moderation_CommentConnection on CommentConnection {
|
fragment CoralAdmin_Moderation_CommentConnection on CommentConnection {
|
||||||
@@ -32,6 +33,7 @@ const slots = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
class UserDetailContainer extends React.Component {
|
class UserDetailContainer extends React.Component {
|
||||||
|
isLoadingMore = false;
|
||||||
|
|
||||||
// status can be 'ACCEPTED' or 'REJECTED'
|
// status can be 'ACCEPTED' or 'REJECTED'
|
||||||
bulkSetCommentStatus = (status) => {
|
bulkSetCommentStatus = (status) => {
|
||||||
@@ -40,7 +42,6 @@ class UserDetailContainer extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Promise.all(changes).then(() => {
|
Promise.all(changes).then(() => {
|
||||||
this.props.data.refetch(); // some comments may have moved out of this tab
|
|
||||||
this.props.clearUserDetailSelections(); // un-select everything
|
this.props.clearUserDetailSelections(); // un-select everything
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -61,12 +62,53 @@ class UserDetailContainer extends React.Component {
|
|||||||
return this.props.setCommentStatus({commentId, status: 'REJECTED'});
|
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 () {
|
render () {
|
||||||
if (!this.props.userId) {
|
if (!this.props.userId) {
|
||||||
return null;
|
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 <UserDetail
|
return <UserDetail
|
||||||
bulkReject={this.bulkReject}
|
bulkReject={this.bulkReject}
|
||||||
@@ -76,10 +118,20 @@ class UserDetailContainer extends React.Component {
|
|||||||
acceptComment={this.acceptComment}
|
acceptComment={this.acceptComment}
|
||||||
rejectComment={this.rejectComment}
|
rejectComment={this.rejectComment}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
loadMore={this.loadMore}
|
||||||
{...this.props} />;
|
{...this.props} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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`
|
export const withUserDetailQuery = withQuery(gql`
|
||||||
query CoralAdmin_UserDetail($author_id: ID!, $statuses: [COMMENT_STATUS!]) {
|
query CoralAdmin_UserDetail($author_id: ID!, $statuses: [COMMENT_STATUS!]) {
|
||||||
user(id: $author_id) {
|
user(id: $author_id) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Comment from '../containers/Comment';
|
|||||||
import styles from './styles.css';
|
import styles from './styles.css';
|
||||||
import EmptyCard from '../../../components/EmptyCard';
|
import EmptyCard from '../../../components/EmptyCard';
|
||||||
import {actionsMap} from '../../../utils/moderationQueueActionsMap';
|
import {actionsMap} from '../../../utils/moderationQueueActionsMap';
|
||||||
import LoadMore from './LoadMore';
|
import LoadMore from '../../../components/LoadMore';
|
||||||
import t from 'coral-framework/services/i18n';
|
import t from 'coral-framework/services/i18n';
|
||||||
import {CSSTransitionGroup} from 'react-transition-group';
|
import {CSSTransitionGroup} from 'react-transition-group';
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
.tabIcon {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
@@ -499,4 +479,4 @@ span {
|
|||||||
right: 0px;
|
right: 0px;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ export default class Comment extends React.Component {
|
|||||||
<div className={commentClassName}>
|
<div className={commentClassName}>
|
||||||
|
|
||||||
<Slot
|
<Slot
|
||||||
className={styles.commentAvatar}
|
className={`${styles.commentAvatar} talk-stream-comment-avatar`}
|
||||||
fill="commentAvatar"
|
fill="commentAvatar"
|
||||||
{...slotProps}
|
{...slotProps}
|
||||||
inline
|
inline
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
min-width: 550px;
|
min-width: 550px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: -17px;
|
right: 0px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
transition: transform 500ms ease-in-out;
|
transition: transform 500ms ease-in-out;
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ const errors = require('../../errors');
|
|||||||
const {Error: {ValidationError}} = require('mongoose');
|
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`.
|
* keyed at `key` or an error caught at `errors`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const wrapResponse = (key) => (promise) => {
|
const wrapResponse = (key) => async (promise) => {
|
||||||
return promise.then((value) => {
|
try {
|
||||||
|
let value = await promise;
|
||||||
|
|
||||||
let res = {};
|
let res = {};
|
||||||
if (key) {
|
if (key) {
|
||||||
res[key] = value;
|
res[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}).catch((err) => {
|
} catch (err) {
|
||||||
if (err instanceof errors.APIError) {
|
if (err instanceof errors.APIError) {
|
||||||
return {
|
return {
|
||||||
errors: [err]
|
errors: [err]
|
||||||
@@ -25,7 +28,7 @@ const wrapResponse = (key) => (promise) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = wrapResponse;
|
module.exports = wrapResponse;
|
||||||
|
|||||||
Reference in New Issue
Block a user