mirror of
https://github.com/wassname/talk.git
synced 2026-08-14 12:50:17 +08:00
Merge branch 'master' into smooth-scroll
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -39,6 +39,8 @@ export default class FlagButton extends Component {
|
||||
} else {
|
||||
this.setState({showMenu: true});
|
||||
}
|
||||
} else {
|
||||
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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} />
|
||||
|
||||
Reference in New Issue
Block a user