Merge branch 'master' into smooth-scroll

This commit is contained in:
Kim Gardner
2017-06-28 10:27:00 +01:00
committed by GitHub
32 changed files with 402 additions and 132 deletions
@@ -195,8 +195,16 @@ export default class Comment extends React.Component {
editComment: React.PropTypes.func,
}
editComment = (...args) => {
return this.props.editComment(this.props.comment.id, this.props.asset.id, ...args);
}
onClickEdit (e) {
e.preventDefault();
if (!can(this.props.currentUser, 'INTERACT_WITH_COMMUNITY')) {
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
return;
}
this.setState({isEditing: true});
}
@@ -239,7 +247,8 @@ export default class Comment extends React.Component {
}
if (can(this.props.currentUser, 'INTERACT_WITH_COMMUNITY')) {
this.props.setActiveReplyBox(this.props.comment.id);
return;
} else {
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
}
return;
}
@@ -468,7 +477,7 @@ export default class Comment extends React.Component {
{
this.state.isEditing
? <EditableCommentContent
editComment={this.props.editComment.bind(null, comment.id, asset.id)}
editComment={this.editComment}
addNotification={addNotification}
asset={asset}
comment={comment}
@@ -527,6 +536,7 @@ export default class Comment extends React.Component {
id={comment.id}
author_id={comment.user.id}
postFlag={postFlag}
addNotification={addNotification}
postDontAgree={postDontAgree}
deleteAction={deleteAction}
showSignInDialog={showSignInDialog}
@@ -545,8 +555,8 @@ export default class Comment extends React.Component {
setActiveReplyBox={setActiveReplyBox}
parentId={parentId || comment.id}
addNotification={addNotification}
authorId={currentUser.id}
postComment={postComment}
currentUser={currentUser}
assetId={asset.id}
/>
: null}
@@ -4,6 +4,7 @@ import {CommentForm} from 'coral-plugin-commentbox/CommentForm';
import styles from './Comment.css';
import {CountdownSeconds} from './CountdownSeconds';
import {getEditableUntilDate} from './util';
import {can} from 'coral-framework/services/perms';
import {Icon} from 'coral-ui';
import t from 'coral-framework/services/i18n';
@@ -47,7 +48,6 @@ export class EditableCommentContent extends React.Component {
}
constructor(props) {
super(props);
this.editComment = this.editComment.bind(this);
this.editWindowExpiryTimeout = null;
}
componentDidMount() {
@@ -65,7 +65,12 @@ export class EditableCommentContent extends React.Component {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
}
async editComment(edit) {
editComment = async (edit) => {
if (!can(this.props.currentUser, 'INTERACT_WITH_COMMUNITY')) {
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
return;
}
const {editComment, addNotification, stopEditing} = this.props;
if (typeof editComment !== 'function') {return;}
let response;
@@ -57,7 +57,10 @@ class Stream extends React.Component {
constructor(props) {
super(props);
this.state = resetCursors(this.state, props);
this.state = {
...resetCursors(this.state, props),
keepCommentBox: false,
};
}
componentWillReceiveProps(next) {
@@ -68,6 +71,12 @@ class Stream extends React.Component {
this.setState(resetCursors);
return;
}
// Keep comment box when user was live suspended, banned, ...
if (!this.userIsDegraged(this.props) && this.userIsDegraged(next)) {
this.setState({keepCommentBox: true});
}
if (
prevComments && nextComments &&
nextComments.nodes.length < prevComments.nodes.length
@@ -133,6 +142,10 @@ class Stream extends React.Component {
return view;
}
userIsDegraged({auth: {user}} = this.props) {
return !can(user, 'INTERACT_WITH_COMMUNITY');
}
render() {
const {
commentClassNames,
@@ -150,6 +163,7 @@ class Stream extends React.Component {
pluginProps,
editName
} = this.props;
const {keepCommentBox} = this.state;
const view = this.getVisibleComments();
const open = asset.closedAt === null;
@@ -171,6 +185,8 @@ class Stream extends React.Component {
me.ignoredUsers.find((u) => u.id === comment.user.id)
);
};
const showCommentBox = loggedIn && ((!banned && !temporarilySuspended && !highlightedComment) || keepCommentBox);
return (
<div id="stream">
@@ -199,10 +215,7 @@ class Stream extends React.Component {
editName={editName}
currentUsername={user.username}
/>}
{loggedIn &&
!banned &&
!temporarilySuspended &&
!highlightedComment &&
{showCommentBox &&
<CommentBox
addNotification={this.props.addNotification}
postComment={this.props.postComment}
@@ -211,7 +224,7 @@ class Stream extends React.Component {
assetId={asset.id}
premod={asset.settings.moderation}
isReply={false}
authorId={user.id}
currentUser={user}
charCountEnable={asset.settings.charCountEnable}
maxCharCount={asset.settings.charCount}
/>}
@@ -12,6 +12,8 @@ import {getDefinitionName} from 'coral-framework/utils';
import {withQuery} from 'coral-framework/hocs';
import Embed from '../components/Embed';
import Stream from './Stream';
import {addNotification} from 'coral-framework/actions/notification';
import t from 'coral-framework/services/i18n';
import {setActiveTab} from '../actions/embed';
import {viewAllComments} from '../actions/stream';
@@ -20,12 +22,63 @@ const {logout, checkLogin} = authActions;
const {fetchAssetSuccess} = assetActions;
class EmbedContainer extends React.Component {
subscriptions = [];
subscribeToUpdates(props = this.props) {
if (props.auth.loggedIn) {
const newSubscriptions = [{
document: USER_BANNED_SUBSCRIPTION,
updateQuery: () => {
addNotification('info', t('your_account_has_been_banned'));
},
},
{
document: USER_SUSPENDED_SUBSCRIPTION,
updateQuery: () => {
addNotification('info', t('your_account_has_been_suspended'));
},
},
{
document: USERNAME_REJECTED_SUBSCRIPTION,
updateQuery: () => {
addNotification('info', t('your_username_has_been_rejected'));
},
}];
this.subscriptions = newSubscriptions.map((s) => props.data.subscribeToMore({
document: s.document,
variables: {
user_id: props.auth.user.id,
},
updateQuery: s.updateQuery,
}));
}
}
unsubscribe() {
this.subscriptions.forEach((unsubscribe) => unsubscribe());
this.subscriptions = [];
}
resubscribe(props) {
this.unsubscribe();
this.subscribeToUpdates(props);
}
componentDidMount() {
this.subscribeToUpdates();
}
componentWillUnmount() {
this.unsubscribe();
}
componentWillReceiveProps(nextProps) {
if (this.props.auth.loggedIn !== nextProps.auth.loggedIn) {
// Refetch after login/logout.
this.props.data.refetch();
this.resubscribe(nextProps);
}
const {fetchAssetSuccess} = this.props;
@@ -52,6 +105,45 @@ class EmbedContainer extends React.Component {
}
}
const USER_BANNED_SUBSCRIPTION = gql`
subscription UserBanned($user_id: ID!) {
userBanned(user_id: $user_id){
id
status
canEditName
suspension {
until
}
}
}
`;
const USER_SUSPENDED_SUBSCRIPTION = gql`
subscription UserSuspended($user_id: ID!) {
userSuspended(user_id: $user_id){
id
status
canEditName
suspension {
until
}
}
}
`;
const USERNAME_REJECTED_SUBSCRIPTION = gql`
subscription UsernameRejected($user_id: ID!) {
usernameRejected(user_id: $user_id){
id
status
canEditName
suspension {
until
}
}
}
`;
const EMBED_QUERY = gql`
query CoralEmbedStream_Embed($assetId: ID, $assetUrl: String, $commentId: ID!, $hasComment: Boolean!, $excludeIgnored: Boolean) {
asset(id: $assetId, url: $assetUrl) {
@@ -95,6 +187,7 @@ const mapDispatchToProps = (dispatch) =>
setActiveTab,
viewAllComments,
fetchAssetSuccess,
addNotification,
},
dispatch
);
@@ -30,11 +30,8 @@ class StreamContainer extends React.Component {
subscriptions = [];
subscribeToUpdates() {
const sub1 = this.props.data.subscribeToMore({
const newSubscriptions = [{
document: COMMENTS_EDITED_SUBSCRIPTION,
variables: {
assetId: this.props.root.asset.id,
},
updateQuery: (prev, {subscriptionData: {data: {commentEdited}}}) => {
// Ignore mutations from me.
@@ -52,13 +49,9 @@ class StreamContainer extends React.Component {
return removeCommentFromEmbedQuery(prev, commentEdited.id);
}
},
});
const sub2 = this.props.data.subscribeToMore({
},
{
document: COMMENTS_ADDED_SUBSCRIPTION,
variables: {
assetId: this.props.root.asset.id,
},
updateQuery: (prev, {subscriptionData: {data: {commentAdded}}}) => {
// Ignore mutations from me.
@@ -81,9 +74,15 @@ class StreamContainer extends React.Component {
return insertCommentIntoEmbedQuery(prev, commentAdded);
}
});
}];
this.subscriptions.push(sub1, sub2);
this.subscriptions = newSubscriptions.map((s) => this.props.data.subscribeToMore({
document: s.document,
variables: {
assetId: this.props.root.asset.id,
},
updateQuery: s.updateQuery,
}));
}
unsubscribe() {
@@ -173,7 +172,7 @@ const commentFragment = gql`
`;
const COMMENTS_ADDED_SUBSCRIPTION = gql`
subscription onCommentAdded($assetId: ID!, $excludeIgnored: Boolean){
subscription CommentAdded($assetId: ID!, $excludeIgnored: Boolean){
commentAdded(asset_id: $assetId){
parent {
id
@@ -185,7 +184,7 @@ const COMMENTS_ADDED_SUBSCRIPTION = gql`
`;
const COMMENTS_EDITED_SUBSCRIPTION = gql`
subscription onCommentEdited($assetId: ID!){
subscription CommentEdited($assetId: ID!){
commentEdited(asset_id: $assetId){
id
body
+7 -5
View File
@@ -28,6 +28,7 @@ const snackbarStyles = {
// This function should return value of window.Coral
const Coral = {};
const Talk = (Coral.Talk = {});
let notificationTimeout = null;
// build the URL to load in the pym iframe
function buildStreamIframeUrl(talkBaseUrl, query) {
@@ -110,14 +111,15 @@ function configurePymParent(pymParent, opts) {
snackbar.className = `coral-notif-${type}`;
snackbar.textContent = text;
setTimeout(() => {
clearTimeout(notificationTimeout);
notificationTimeout = setTimeout(() => {
snackbar.style.transform = 'translate(-50%, 0)';
snackbar.style.opacity = 1;
}, 0);
setTimeout(() => {
snackbar.style.opacity = 0;
}, 5000);
notificationTimeout = setTimeout(() => {
snackbar.style.opacity = 0;
}, 7000);
}, 0);
});
// Helps child show notifications at the right scrollTop
+14
View File
@@ -151,6 +151,20 @@ export default function auth (state = initialState, action) {
case actions.SET_REDIRECT_URI:
return state
.set('redirectUri', action.uri);
case 'APOLLO_SUBSCRIPTION_RESULT':
if (action.operationName === 'UserBanned' && state.getIn(['user', 'id']) === action.variables.user_id) {
return state
.mergeIn(['user'], action.result.data.userBanned);
}
if (action.operationName === 'UserSuspended' && state.getIn(['user', 'id']) === action.variables.user_id) {
return state
.mergeIn(['user'], action.result.data.userSuspended);
}
if (action.operationName === 'UsernameRejected' && state.getIn(['user', 'id']) === action.variables.user_id) {
return state
.mergeIn(['user'], action.result.data.usernameRejected);
}
return state;
default :
return state;
}
+10 -3
View File
@@ -1,6 +1,7 @@
import React, {PropTypes} from 'react';
import t from 'coral-framework/services/i18n';
import {can} from 'coral-framework/services/perms';
import Slot from 'coral-framework/components/Slot';
import {connect} from 'react-redux';
@@ -43,8 +44,14 @@ class CommentBox extends React.Component {
assetId,
parentId,
addNotification,
currentUser,
} = this.props;
if (!can(currentUser, 'INTERACT_WITH_COMMUNITY')) {
addNotification('error', t('error.NOT_AUTHORIZED'));
return;
}
let comment = {
asset_id: assetId,
parent_id: parentId,
@@ -126,7 +133,7 @@ class CommentBox extends React.Component {
handleChange = (e) => this.setState({body: e.target.value});
render () {
const {styles, isReply, authorId, maxCharCount} = this.props;
const {styles, isReply, currentUser, maxCharCount} = this.props;
let {cancelButtonClicked} = this.props;
if (isReply && typeof cancelButtonClicked !== 'function') {
@@ -145,7 +152,7 @@ class CommentBox extends React.Component {
charCountEnable={this.props.charCountEnable}
bodyPlaceholder={t('comment.comment')}
bodyInputId={isReply ? 'replyText' : 'commentText'}
saveComment={authorId && this.postComment}
saveComment={currentUser && this.postComment}
buttonContainerStart={<Slot
fill="commentInputDetailArea"
registerHook={this.registerHook}
@@ -170,7 +177,7 @@ CommentBox.propTypes = {
cancelButtonClicked: PropTypes.func,
assetId: PropTypes.string.isRequired,
parentId: PropTypes.string,
authorId: PropTypes.string.isRequired,
currentUser: PropTypes.object.isRequired,
isReply: PropTypes.bool.isRequired,
canPost: PropTypes.bool,
};
+2
View File
@@ -39,6 +39,8 @@ export default class FlagButton extends Component {
} else {
this.setState({showMenu: true});
}
} else {
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
}
}
+2 -2
View File
@@ -18,7 +18,7 @@ class ReplyBox extends Component {
styles,
postComment,
assetId,
authorId,
currentUser,
addNotification,
parentId,
commentPostedHandler,
@@ -33,7 +33,7 @@ class ReplyBox extends Component {
parentId={parentId}
cancelButtonClicked={this.cancelReply}
addNotification={addNotification}
authorId={authorId}
currentUser={currentUser}
assetId={assetId}
postComment={postComment}
isReply={true} />