diff --git a/bin/cli-users b/bin/cli-users
index 6e901f529..ea308ae44 100755
--- a/bin/cli-users
+++ b/bin/cli-users
@@ -8,6 +8,7 @@ const util = require('./util');
const program = require('commander');
const inquirer = require('inquirer');
const { graphql } = require('graphql');
+const helpers = require('../services/migration/helpers');
const { stripIndent } = require('common-tags');
const Table = require('cli-table');
@@ -28,11 +29,23 @@ const CommentModel = require('../models/comment');
const ActionModel = require('../models/action');
const USER_ROLES = require('../models/enum/user_roles');
const mongoose = require('../services/mongoose');
-const databaseVerifications = require('./verifications/database');
// Register the shutdown criteria.
util.onshutdown([() => mongoose.disconnect()]);
+/**
+ * transforms a specific action to a removal action on the target model.
+ */
+const actionDecrTransformer = ({ item_id, action_type, group_id }) => ({
+ query: { id: item_id },
+ update: {
+ $inc: {
+ [`action_counts.${action_type.toLowerCase()}`]: -1,
+ [`action_counts.${action_type.toLowerCase()}_${group_id.toLowerCase()}`]: -1,
+ },
+ },
+});
+
/**
* Deletes a user and cleans up their associated verifications.
*/
@@ -63,8 +76,27 @@ async function deleteUser(userID) {
return util.shutdown();
}
+ const { transformSingleWithCursor } = helpers({
+ queryBatchSize: 10000,
+ updateBatchSize: 10000,
+ });
+
console.warn("Removing user's actions");
+ // Remove all actions against comments.
+ await transformSingleWithCursor(
+ ActionModel.collection.find({ user_id: user.id, item_type: 'COMMENTS' }),
+ actionDecrTransformer,
+ CommentModel
+ );
+
+ // Remove all actions against users.
+ await transformSingleWithCursor(
+ ActionModel.collection.find({ user_id: user.id, item_type: 'USERS' }),
+ actionDecrTransformer,
+ UserModel
+ );
+
// Remove all the user's actions.
await ActionModel.where({ user_id: user.id })
.setOptions({ multi: true })
@@ -72,18 +104,34 @@ async function deleteUser(userID) {
console.warn("Removing user's comments");
+ // Removes all the user's reply counts on each of the comments that they
+ // have commented on.
+ await transformSingleWithCursor(
+ CommentModel.collection.aggregate([
+ { $match: { author_id: user.id } },
+ {
+ $group: {
+ _id: '$parent_id',
+ count: { $sum: 1 },
+ },
+ },
+ ]),
+ ({ _id: parent_id, count }) => ({
+ query: { id: parent_id },
+ update: {
+ $inc: {
+ reply_count: -1 * count,
+ },
+ },
+ }),
+ CommentModel
+ );
+
// Remove all the user's comments.
await CommentModel.where({ author_id: user.id })
.setOptions({ multi: true })
.remove();
- console.warn('Updating the database indexes');
-
- // Update the counts that might have changed.
- for (const verification of databaseVerifications) {
- await verification({ fix: true, limit: Infinity, batch: 1000 });
- }
-
console.warn('Removing the user');
// Remove the user.
diff --git a/client/coral-admin/src/components/ApproveButton.css b/client/coral-admin/src/components/ApproveButton.css
index 071be2fcb..db3ee63f6 100644
--- a/client/coral-admin/src/components/ApproveButton.css
+++ b/client/coral-admin/src/components/ApproveButton.css
@@ -5,16 +5,11 @@
background: white;
padding: 10px 12px;
box-sizing: border-box;
- vertical-align: middle;
- line-height: 24px;
- font-size: 17px;
- height: 47px;
border-radius: 3px;
text-transform: capitalize;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.03), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.09);
- width: 129px;
- transform: scale(.8);
- margin: 0;
+ width: 100%;
+ margin: 0 0 .5em;
&:not(:disabled):hover {
box-shadow: none;
diff --git a/client/coral-admin/src/components/RejectButton.css b/client/coral-admin/src/components/RejectButton.css
index 3885ebf9d..62a061fa4 100644
--- a/client/coral-admin/src/components/RejectButton.css
+++ b/client/coral-admin/src/components/RejectButton.css
@@ -5,16 +5,11 @@
background: white;
padding: 10px 11px;
box-sizing: border-box;
- vertical-align: middle;
- line-height: 24px;
- font-size: 17px;
- height: 47px;
border-radius: 3px;
text-transform: capitalize;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.03), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.09);
- width: 129px;
- transform: scale(.8);
- margin: 0;
+ width: 100%;
+ margin: 0 0 .5em;
&:not(:disabled):hover {
color: white;
diff --git a/client/coral-admin/src/components/UserDetailComment.css b/client/coral-admin/src/components/UserDetailComment.css
index 50e37e28e..75b968625 100644
--- a/client/coral-admin/src/components/UserDetailComment.css
+++ b/client/coral-admin/src/components/UserDetailComment.css
@@ -81,6 +81,8 @@
}
.sideActions {
+ /* IE 11 needs this... */
+ width: 105px;
}
.editedMarker {
diff --git a/client/coral-admin/src/constants/install.js b/client/coral-admin/src/constants/install.js
index fb2cc4bef..034453ecc 100644
--- a/client/coral-admin/src/constants/install.js
+++ b/client/coral-admin/src/constants/install.js
@@ -2,8 +2,8 @@ export const NEXT_STEP = 'NEXT_STEP';
export const GO_TO_STEP = 'GO_TO_STEP';
export const PREVIOUS_STEP = 'PREVIOUS_STEP';
export const ADD_ERROR = 'ADD_ERROR';
-export const HAS_ERROR = 'HAS_ERROR';
-export const SHOW_ERRORS = 'SHOW_ERRORS';
+export const HAS_ERROR = 'HAS_ERROR';
+export const SHOW_ERRORS = 'SHOW_ERRORS';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
export const INSTALL_REQUEST = 'INSTALL_REQUEST';
export const INSTALL_SUCCESS = 'INSTALL_SUCCESS';
diff --git a/client/coral-admin/src/routes/Community/components/FlaggedUser.js b/client/coral-admin/src/routes/Community/components/FlaggedUser.js
index 00377e679..72a46e27e 100644
--- a/client/coral-admin/src/routes/Community/components/FlaggedUser.js
+++ b/client/coral-admin/src/routes/Community/components/FlaggedUser.js
@@ -3,7 +3,7 @@ import styles from './FlaggedUser.css';
import PropTypes from 'prop-types';
import cn from 'classnames';
import t from 'coral-framework/services/i18n';
-import { username } from 'talk-plugin-flags/helpers/flagReasons';
+import { username } from 'coral-framework/graphql/flagReasons';
import ApproveButton from 'coral-admin/src/components/ApproveButton';
import RejectButton from 'coral-admin/src/components/RejectButton';
import { isFlaggedUserDangling } from '../utils';
diff --git a/client/coral-embed-stream/src/AppRouter.js b/client/coral-embed-stream/src/AppRouter.js
index 4b101fb61..ec7b6300a 100644
--- a/client/coral-embed-stream/src/AppRouter.js
+++ b/client/coral-embed-stream/src/AppRouter.js
@@ -3,7 +3,7 @@ import { Router, Route } from 'react-router';
import PropTypes from 'prop-types';
import Embed from './containers/Embed';
-import { LoginContainer } from 'coral-sign-in/containers/LoginContainer';
+import { LoginContainer } from './containers/LoginContainer';
const routes = (
diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js
index 8af2dfef2..bc31c90e9 100644
--- a/client/coral-embed-stream/src/actions/stream.js
+++ b/client/coral-embed-stream/src/actions/stream.js
@@ -70,3 +70,17 @@ export const removeCommentClassName = idx => ({
export const setActiveTab = tab => dispatch => {
dispatch({ type: actions.SET_ACTIVE_TAB, tab });
};
+
+export const addCommentBoxTag = tag => ({
+ type: actions.ADD_COMMENT_BOX_TAG,
+ tag,
+});
+
+export const removeCommentBoxTag = idx => ({
+ type: actions.REMOVE_COMMENT_BOX_TAG,
+ idx,
+});
+
+export const clearCommentBoxTags = () => ({
+ type: actions.CLEAR_COMMENT_BOX_TAGS,
+});
diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js
index e64a54caa..980e5745e 100644
--- a/client/coral-embed-stream/src/components/Embed.js
+++ b/client/coral-embed-stream/src/components/Embed.js
@@ -9,7 +9,7 @@ import AutomaticAssetClosure from '../containers/AutomaticAssetClosure';
import ExtendableTabPanel from '../containers/ExtendableTabPanel';
import { Tab, TabPane } from 'coral-ui';
-import ProfileContainer from 'coral-settings/containers/ProfileContainer';
+import ProfileContainer from '../tabs/profile/containers/ProfileContainer';
import Popup from 'coral-framework/components/Popup';
import IfSlotIsNotEmpty from 'coral-framework/components/IfSlotIsNotEmpty';
import cn from 'classnames';
diff --git a/client/coral-embed-stream/src/constants/stream.js b/client/coral-embed-stream/src/constants/stream.js
index b4deb24e1..09d069851 100644
--- a/client/coral-embed-stream/src/constants/stream.js
+++ b/client/coral-embed-stream/src/constants/stream.js
@@ -1,3 +1,5 @@
+const prefix = 'TALK_EMBED_STREAM';
+
export const SET_ACTIVE_REPLY_BOX = 'SET_ACTIVE_REPLY_BOX';
export const ADDTL_COMMENTS_ON_LOAD_MORE = 10;
export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS';
@@ -7,3 +9,6 @@ export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME';
export const THREADING_LEVEL = process.env.TALK_THREADING_LEVEL;
export const SET_ACTIVE_TAB = 'CORAL_STREAM_SET_ACTIVE_TAB';
export const SET_SORT = 'CORAL_STREAM_SET_SORT';
+export const ADD_COMMENT_BOX_TAG = `${prefix}_COMMENT_BOX_ADD_TAG`;
+export const REMOVE_COMMENT_BOX_TAG = `${prefix}_COMMENT_BOX_REMOVE_TAG`;
+export const CLEAR_COMMENT_BOX_TAGS = `${prefix}_COMMENT_BOX_CLEAR_TAGS`;
diff --git a/client/coral-sign-in/containers/LoginContainer.js b/client/coral-embed-stream/src/containers/LoginContainer.js
similarity index 100%
rename from client/coral-sign-in/containers/LoginContainer.js
rename to client/coral-embed-stream/src/containers/LoginContainer.js
diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js
index 279a1dcf6..0a091e97d 100644
--- a/client/coral-embed-stream/src/graphql/index.js
+++ b/client/coral-embed-stream/src/graphql/index.js
@@ -93,9 +93,6 @@ export default {
name
created_at
}
- assigned_by {
- id
- }
}
user {
id
diff --git a/client/coral-embed-stream/src/reducers/index.js b/client/coral-embed-stream/src/reducers/index.js
index e895a70df..ac581557b 100644
--- a/client/coral-embed-stream/src/reducers/index.js
+++ b/client/coral-embed-stream/src/reducers/index.js
@@ -4,12 +4,10 @@ import embed from './embed';
import config from './config';
import configure from './configure';
import stream from './stream';
-import { reducer as commentBox } from '../../../talk-plugin-commentbox';
export default {
auth,
asset,
- commentBox,
embed,
config,
configure,
diff --git a/client/coral-embed-stream/src/reducers/stream.js b/client/coral-embed-stream/src/reducers/stream.js
index b5afddafc..42af4453a 100644
--- a/client/coral-embed-stream/src/reducers/stream.js
+++ b/client/coral-embed-stream/src/reducers/stream.js
@@ -25,6 +25,7 @@ const initialState = {
previousTab: '',
sortBy: 'CREATED_AT',
sortOrder: 'DESC',
+ commentBoxTags: [],
};
export default function stream(state = initialState, action) {
@@ -74,6 +75,24 @@ export default function stream(state = initialState, action) {
sortOrder: action.sortOrder ? action.sortOrder : state.sortOrder,
sortBy: action.sortBy ? action.sortBy : state.sortBy,
};
+ case actions.ADD_COMMENT_BOX_TAG:
+ return {
+ ...state,
+ commentBoxTags: [...state.commentBoxTags, action.tag],
+ };
+ case actions.REMOVE_COMMENT_BOX_TAG:
+ return {
+ ...state,
+ commentBoxTags: [
+ ...state.commentBoxTags.slice(0, action.idx),
+ ...state.commentBoxTags.slice(action.idx + 1),
+ ],
+ };
+ case actions.CLEAR_COMMENT_BOX_TAGS:
+ return {
+ ...state,
+ commentBoxTags: [],
+ };
default:
return state;
}
diff --git a/client/talk-plugin-history/Comment.css b/client/coral-embed-stream/src/tabs/profile/components/Comment.css
similarity index 100%
rename from client/talk-plugin-history/Comment.css
rename to client/coral-embed-stream/src/tabs/profile/components/Comment.css
diff --git a/client/talk-plugin-history/Comment.js b/client/coral-embed-stream/src/tabs/profile/components/Comment.js
similarity index 99%
rename from client/talk-plugin-history/Comment.js
rename to client/coral-embed-stream/src/tabs/profile/components/Comment.js
index 015044b37..fec5863a2 100644
--- a/client/talk-plugin-history/Comment.js
+++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Icon } from '../coral-ui';
+import { Icon } from 'coral-ui';
import styles from './Comment.css';
import Slot from 'coral-framework/components/Slot';
import CommentTimestamp from 'coral-framework/components/CommentTimestamp';
diff --git a/client/talk-plugin-history/CommentHistory.js b/client/coral-embed-stream/src/tabs/profile/components/CommentHistory.js
similarity index 100%
rename from client/talk-plugin-history/CommentHistory.js
rename to client/coral-embed-stream/src/tabs/profile/components/CommentHistory.js
diff --git a/client/talk-plugin-history/LoadMore.js b/client/coral-embed-stream/src/tabs/profile/components/LoadMore.js
similarity index 100%
rename from client/talk-plugin-history/LoadMore.js
rename to client/coral-embed-stream/src/tabs/profile/components/LoadMore.js
diff --git a/client/coral-settings/components/NotLoggedIn.css b/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css
similarity index 100%
rename from client/coral-settings/components/NotLoggedIn.css
rename to client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css
diff --git a/client/coral-settings/components/NotLoggedIn.js b/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js
similarity index 100%
rename from client/coral-settings/components/NotLoggedIn.js
rename to client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js
diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-embed-stream/src/tabs/profile/containers/ProfileContainer.js
similarity index 98%
rename from client/coral-settings/containers/ProfileContainer.js
rename to client/coral-embed-stream/src/tabs/profile/containers/ProfileContainer.js
index e1bcf7f13..a818ebec4 100644
--- a/client/coral-settings/containers/ProfileContainer.js
+++ b/client/coral-embed-stream/src/tabs/profile/containers/ProfileContainer.js
@@ -8,7 +8,7 @@ import cn from 'classnames';
import { link } from 'coral-framework/services/pym';
import NotLoggedIn from '../components/NotLoggedIn';
import { Spinner } from 'coral-ui';
-import CommentHistory from 'talk-plugin-history/CommentHistory';
+import CommentHistory from '../components/CommentHistory';
// TODO: Auth logic needs refactoring.
import {
diff --git a/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js b/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js
new file mode 100644
index 000000000..3a0ba5790
--- /dev/null
+++ b/client/coral-embed-stream/src/tabs/stream/components/ChangedUsername.js
@@ -0,0 +1,17 @@
+import React, { Component } from 'react';
+import t from 'coral-framework/services/i18n';
+import RestrictedMessageBox from 'coral-framework/components/RestrictedMessageBox';
+
+class ChangeUsername extends Component {
+ render() {
+ return (
+
+
+ {t('framework.changed_name.msg')}
+
+
+ );
+ }
+}
+
+export default ChangeUsername;
diff --git a/client/coral-embed-stream/src/tabs/stream/components/Comment.js b/client/coral-embed-stream/src/tabs/stream/components/Comment.js
index cd0fab9e9..1b4beb14d 100644
--- a/client/coral-embed-stream/src/tabs/stream/components/Comment.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/Comment.js
@@ -1,10 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
-import TagLabel from 'talk-plugin-tag-label/TagLabel';
+import TagLabel from './TagLabel';
import CommentTimestamp from 'coral-framework/components/CommentTimestamp';
-import { ReplyBox, ReplyButton } from 'talk-plugin-replies';
-import { FlagComment } from 'talk-plugin-flags';
+import ReplyButton from './ReplyButton';
+import ReplyBox from './ReplyBox';
+import FlagComment from './FlagComment';
import { can } from 'coral-framework/services/perms';
import { TransitionGroup } from 'react-transition-group';
import cn from 'classnames';
@@ -79,6 +80,11 @@ const ActionButton = ({ children }) => {
);
};
+ActionButton.propTypes = {
+ // id of currently opened ReplyBox. tracked in Stream.js
+ children: PropTypes.node,
+};
+
// Determine whether the comment with id is in the part of the comments tree.
function containsCommentId(props, id) {
if (props.comment.id === id) {
@@ -154,6 +160,7 @@ export default class Comment extends React.Component {
static propTypes = {
// id of currently opened ReplyBox. tracked in Stream.js
+ children: PropTypes.node,
activeReplyBox: PropTypes.string.isRequired,
disableReply: PropTypes.bool,
setActiveReplyBox: PropTypes.func.isRequired,
@@ -172,6 +179,12 @@ export default class Comment extends React.Component {
}),
charCountEnable: PropTypes.bool.isRequired,
maxCharCount: PropTypes.number,
+ data: PropTypes.object,
+ root: PropTypes.object,
+ loadMore: PropTypes.func,
+ postDontAgree: PropTypes.func,
+ animateEnter: PropTypes.bool,
+ commentClassNames: PropTypes.array,
comment: PropTypes.shape({
depth: PropTypes.number,
action_summaries: PropTypes.array.isRequired,
diff --git a/client/talk-plugin-commentbox/styles.css b/client/coral-embed-stream/src/tabs/stream/components/CommentForm.css
similarity index 100%
rename from client/talk-plugin-commentbox/styles.css
rename to client/coral-embed-stream/src/tabs/stream/components/CommentForm.css
diff --git a/client/talk-plugin-commentbox/CommentForm.js b/client/coral-embed-stream/src/tabs/stream/components/CommentForm.js
similarity index 96%
rename from client/talk-plugin-commentbox/CommentForm.js
rename to client/coral-embed-stream/src/tabs/stream/components/CommentForm.js
index 62604d88f..9a84bdbe8 100644
--- a/client/talk-plugin-commentbox/CommentForm.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/CommentForm.js
@@ -4,8 +4,9 @@ import { Button } from 'coral-ui';
import cn from 'classnames';
import Slot from 'coral-framework/components/Slot';
-import { name } from './CommentBox';
-import styles from './styles.css';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
+import { name } from '../containers/CommentBox';
+import styles from './CommentForm.css';
import t from 'coral-framework/services/i18n';
diff --git a/client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js b/client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js
index f4cfc42e8..ac50df9af 100644
--- a/client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js
@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { notifyForNewCommentStatus } from 'talk-plugin-commentbox/CommentBox';
-import { CommentForm } from 'talk-plugin-commentbox/CommentForm';
+
+import { notifyForNewCommentStatus } from '../helpers';
+import { CommentForm } from './CommentForm';
import styles from './Comment.css';
import { CountdownSeconds } from './CountdownSeconds';
import { getEditableUntilDate } from './util';
@@ -43,6 +44,8 @@ export class EditableCommentContent extends React.Component {
stopEditing: PropTypes.func,
};
+ unmounted = false;
+
constructor(props) {
super(props);
this.editWindowExpiryTimeout = null;
@@ -62,6 +65,7 @@ export class EditableCommentContent extends React.Component {
}
}
componentWillUnmount() {
+ this.unmounted = true;
if (this.editWindowExpiryTimeout) {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
@@ -86,7 +90,9 @@ export class EditableCommentContent extends React.Component {
let response;
try {
response = await editComment({ body: this.state.body });
- this.setState({ loadingState: 'success' });
+ if (!this.unmounted) {
+ this.setState({ loadingState: 'success' });
+ }
const status = response.data.editComment.comment.status;
notifyForNewCommentStatus(this.props.notify, status);
if (typeof stopEditing === 'function') {
diff --git a/client/talk-plugin-flags/components/styles.css b/client/coral-embed-stream/src/tabs/stream/components/FlagButton.css
similarity index 100%
rename from client/talk-plugin-flags/components/styles.css
rename to client/coral-embed-stream/src/tabs/stream/components/FlagButton.css
diff --git a/client/talk-plugin-flags/components/FlagButton.js b/client/coral-embed-stream/src/tabs/stream/components/FlagButton.js
similarity index 97%
rename from client/talk-plugin-flags/components/FlagButton.js
rename to client/coral-embed-stream/src/tabs/stream/components/FlagButton.js
index b2048e105..bc673ba28 100644
--- a/client/talk-plugin-flags/components/FlagButton.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/FlagButton.js
@@ -6,11 +6,12 @@ import { can } from 'coral-framework/services/perms';
import { PopupMenu, Button } from 'coral-ui';
import ClickOutside from 'coral-framework/components/ClickOutside';
import cn from 'classnames';
-import styles from './styles.css';
-import * as REASONS from '../helpers/flagReasons';
+import styles from './FlagButton.css';
+import * as REASONS from 'coral-framework/graphql/flagReasons';
import { getErrorMessages, forEachError } from 'coral-framework/utils';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
const name = 'talk-plugin-flags';
export default class FlagButton extends Component {
diff --git a/client/talk-plugin-flags/components/FlagComment.js b/client/coral-embed-stream/src/tabs/stream/components/FlagComment.js
similarity index 95%
rename from client/talk-plugin-flags/components/FlagComment.js
rename to client/coral-embed-stream/src/tabs/stream/components/FlagComment.js
index bd8607c6b..48379f7dd 100644
--- a/client/talk-plugin-flags/components/FlagComment.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/FlagComment.js
@@ -2,7 +2,7 @@ import React from 'react';
import FlagButton from './FlagButton';
import t from 'coral-framework/services/i18n';
-import { username, comment } from '../helpers/flagReasons';
+import { username, comment } from 'coral-framework/graphql/flagReasons';
const FlagComment = props => (
diff --git a/client/talk-plugin-infobox/InfoBox.js b/client/coral-embed-stream/src/tabs/stream/components/InfoBox.js
similarity index 80%
rename from client/talk-plugin-infobox/InfoBox.js
rename to client/coral-embed-stream/src/tabs/stream/components/InfoBox.js
index 8577dbc9b..7b811b63c 100644
--- a/client/talk-plugin-infobox/InfoBox.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/InfoBox.js
@@ -1,6 +1,7 @@
import React from 'react';
import Markdown from 'coral-framework/components/Markdown';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
const packagename = 'talk-plugin-infobox';
const InfoBox = ({ enable, content }) => (
diff --git a/client/talk-plugin-moderation/styles.css b/client/coral-embed-stream/src/tabs/stream/components/ModerationLink.css
similarity index 100%
rename from client/talk-plugin-moderation/styles.css
rename to client/coral-embed-stream/src/tabs/stream/components/ModerationLink.css
diff --git a/client/talk-plugin-moderation/ModerationLink.js b/client/coral-embed-stream/src/tabs/stream/components/ModerationLink.js
similarity index 94%
rename from client/talk-plugin-moderation/ModerationLink.js
rename to client/coral-embed-stream/src/tabs/stream/components/ModerationLink.js
index c40c50b82..d33f18a5e 100644
--- a/client/talk-plugin-moderation/ModerationLink.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/ModerationLink.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import styles from './styles.css';
+import styles from './ModerationLink.css';
import cn from 'classnames';
import t from 'coral-framework/services/i18n';
diff --git a/client/talk-plugin-replies/ReplyBox.js b/client/coral-embed-stream/src/tabs/stream/components/ReplyBox.js
similarity index 91%
rename from client/talk-plugin-replies/ReplyBox.js
rename to client/coral-embed-stream/src/tabs/stream/components/ReplyBox.js
index 2a6bd017b..6c93cb7fe 100644
--- a/client/talk-plugin-replies/ReplyBox.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/ReplyBox.js
@@ -1,7 +1,8 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import CommentBox from '../talk-plugin-commentbox/CommentBox';
+import CommentBox from '../containers/CommentBox';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
const name = 'talk-plugin-replies';
class ReplyBox extends Component {
diff --git a/client/talk-plugin-replies/ReplyButton.css b/client/coral-embed-stream/src/tabs/stream/components/ReplyButton.css
similarity index 100%
rename from client/talk-plugin-replies/ReplyButton.css
rename to client/coral-embed-stream/src/tabs/stream/components/ReplyButton.css
diff --git a/client/talk-plugin-replies/ReplyButton.js b/client/coral-embed-stream/src/tabs/stream/components/ReplyButton.js
similarity index 90%
rename from client/talk-plugin-replies/ReplyButton.js
rename to client/coral-embed-stream/src/tabs/stream/components/ReplyButton.js
index 66433bdba..812811741 100644
--- a/client/talk-plugin-replies/ReplyButton.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/ReplyButton.js
@@ -6,6 +6,7 @@ import t from 'coral-framework/services/i18n';
import cn from 'classnames';
import styles from './ReplyButton.css';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
const name = 'talk-plugin-replies';
const ReplyButton = ({ onClick }) => {
diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js
index 6288a2b6b..23f438045 100644
--- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js
@@ -5,12 +5,12 @@ import Comment from '../containers/Comment';
import BannedAccount from '../../../components/BannedAccount';
import ChangeUsername from '../containers/ChangeUsername';
import Slot from 'coral-framework/components/Slot';
-import InfoBox from 'talk-plugin-infobox/InfoBox';
+import InfoBox from './InfoBox';
import { can } from 'coral-framework/services/perms';
-import { ModerationLink } from 'talk-plugin-moderation';
+import ModerationLink from './ModerationLink';
import RestrictedMessageBox from 'coral-framework/components/RestrictedMessageBox';
import t, { timeago } from 'coral-framework/services/i18n';
-import CommentBox from 'talk-plugin-commentbox/CommentBox';
+import CommentBox from '../containers/CommentBox';
import QuestionBox from '../../../components/QuestionBox';
import { isCommentActive } from 'coral-framework/utils';
import { Tab, TabCount, TabPane } from 'coral-ui';
@@ -25,6 +25,7 @@ import AllCommentsPane from './AllCommentsPane';
import ExtendableTabPanel from '../../../containers/ExtendableTabPanel';
import styles from './Stream.css';
+import ChangedUsername from './ChangedUsername';
class Stream extends React.Component {
constructor(props) {
@@ -237,6 +238,7 @@ class Stream extends React.Component {
const banned = get(user, 'status.banned.status');
const suspensionUntil = get(user, 'status.suspension.until');
const rejectedUsername = get(user, 'status.username.status') === 'REJECTED';
+ const changedUsername = get(user, 'status.username.status') === 'CHANGED';
const temporarilySuspended =
user && suspensionUntil && new Date(suspensionUntil) > new Date();
@@ -246,6 +248,7 @@ class Stream extends React.Component {
((!banned &&
!temporarilySuspended &&
!rejectedUsername &&
+ !changedUsername &&
!highlightedComment) ||
keepCommentBox);
const slotProps = { data };
@@ -285,6 +288,7 @@ class Stream extends React.Component {
)}
)}
+ {changedUsername &&
}
{!banned && rejectedUsername &&
}
{banned &&
}
{showCommentBox && (
diff --git a/client/talk-plugin-tag-label/TagLabel.js b/client/coral-embed-stream/src/tabs/stream/components/TagLabel.js
similarity index 66%
rename from client/talk-plugin-tag-label/TagLabel.js
rename to client/coral-embed-stream/src/tabs/stream/components/TagLabel.js
index 27d6bdde4..2b2bee48a 100644
--- a/client/talk-plugin-tag-label/TagLabel.js
+++ b/client/coral-embed-stream/src/tabs/stream/components/TagLabel.js
@@ -1,5 +1,7 @@
import React from 'react';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
+
const TagLabel = ({ children }) => (
{children}
);
diff --git a/client/talk-plugin-infobox/__tests__/InfoBox.spec.js b/client/coral-embed-stream/src/tabs/stream/components/__tests__/InfoBox.spec.js
similarity index 100%
rename from client/talk-plugin-infobox/__tests__/InfoBox.spec.js
rename to client/coral-embed-stream/src/tabs/stream/components/__tests__/InfoBox.spec.js
diff --git a/client/talk-plugin-infobox/__tests__/__snapshots__/InfoBox.spec.js.snap b/client/coral-embed-stream/src/tabs/stream/components/__tests__/__snapshots__/InfoBox.spec.js.snap
similarity index 100%
rename from client/talk-plugin-infobox/__tests__/__snapshots__/InfoBox.spec.js.snap
rename to client/coral-embed-stream/src/tabs/stream/components/__tests__/__snapshots__/InfoBox.spec.js.snap
diff --git a/client/talk-plugin-commentbox/CommentBox.js b/client/coral-embed-stream/src/tabs/stream/containers/CommentBox.js
similarity index 83%
rename from client/talk-plugin-commentbox/CommentBox.js
rename to client/coral-embed-stream/src/tabs/stream/containers/CommentBox.js
index 8eac1ff2f..a9f9123c5 100644
--- a/client/talk-plugin-commentbox/CommentBox.js
+++ b/client/coral-embed-stream/src/tabs/stream/containers/CommentBox.js
@@ -6,32 +6,12 @@ import { can } from 'coral-framework/services/perms';
import Slot from 'coral-framework/components/Slot';
import { connect } from 'react-redux';
-import { CommentForm } from './CommentForm';
+import { CommentForm } from '../components/CommentForm';
+import { notifyForNewCommentStatus } from '../helpers';
+// TODO: (kiwi) Need to adapt CSS classes post refactor to match the rest.
export const name = 'talk-plugin-commentbox';
-const notifyReasons = ['LINKS', 'TRUST'];
-
-function shouldNotify(actions = []) {
- return actions.some(
- ({ __typename, reason }) =>
- __typename === 'FlagAction' && notifyReasons.includes(reason)
- );
-}
-
-// Given a newly posted comment's status, show a notification to the user
-// if needed
-export const notifyForNewCommentStatus = (notify, comment, actions) => {
- if (comment.status === 'REJECTED') {
- notify('error', t('comment_box.comment_post_banned_word'));
- } else if (
- comment.status === 'PREMOD' ||
- (comment.status === 'SYSTEM_WITHHELD' && shouldNotify(actions))
- ) {
- notify('success', t('comment_box.comment_post_notif_premod'));
- }
-};
-
/**
* Container for posting a new Comment
*/
@@ -70,7 +50,7 @@ class CommentBox extends React.Component {
asset_id: assetId,
parent_id: parentId,
body: this.state.body,
- ...this.props.commentBox,
+ tags: this.props.tags,
};
// Execute preSubmit Hooks
@@ -86,7 +66,7 @@ class CommentBox extends React.Component {
// Execute postSubmit Hooks
this.state.hooks.postSubmit.forEach(hook => hook(data));
- notifyForNewCommentStatus(notify, postedComment, actions);
+ notifyForNewCommentStatus(notify, postedComment.status, actions);
if (commentPostedHandler) {
commentPostedHandler();
@@ -202,9 +182,11 @@ CommentBox.propTypes = {
isReply: PropTypes.bool.isRequired,
canPost: PropTypes.bool,
notify: PropTypes.func.isRequired,
- commentBox: PropTypes.object,
+ tags: PropTypes.array,
};
-const mapStateToProps = ({ commentBox }) => ({ commentBox });
+const mapStateToProps = state => ({
+ tags: state.stream.commentBoxTags,
+});
export default connect(mapStateToProps, null)(CommentBox);
diff --git a/client/coral-embed-stream/src/tabs/stream/helpers.js b/client/coral-embed-stream/src/tabs/stream/helpers.js
new file mode 100644
index 000000000..f44f777e4
--- /dev/null
+++ b/client/coral-embed-stream/src/tabs/stream/helpers.js
@@ -0,0 +1,23 @@
+import t from 'coral-framework/services/i18n';
+
+const notifyReasons = ['LINKS', 'TRUST'];
+
+function shouldNotify(actions = []) {
+ return actions.some(
+ ({ __typename, reason }) =>
+ __typename === 'FlagAction' && notifyReasons.includes(reason)
+ );
+}
+
+// Given a newly posted or edited comment's status, show a notification to the user
+// if needed
+export const notifyForNewCommentStatus = (notify, status, actions) => {
+ if (status === 'REJECTED') {
+ notify('error', t('comment_box.comment_post_banned_word'));
+ } else if (
+ status === 'PREMOD' ||
+ (status === 'SYSTEM_WITHHELD' && shouldNotify(actions))
+ ) {
+ notify('success', t('comment_box.comment_post_notif_premod'));
+ }
+};
diff --git a/client/talk-plugin-flags/helpers/flagReasons.js b/client/coral-framework/graphql/flagReasons.js
similarity index 100%
rename from client/talk-plugin-flags/helpers/flagReasons.js
rename to client/coral-framework/graphql/flagReasons.js
diff --git a/client/talk-plugin-commentbox/actions.js b/client/talk-plugin-commentbox/actions.js
deleted file mode 100644
index 7a6aa3249..000000000
--- a/client/talk-plugin-commentbox/actions.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export const addTag = tag => ({
- type: 'ADD_TAG',
- tag,
-});
-
-export const removeTag = idx => ({
- type: 'REMOVE_TAG',
- idx,
-});
-
-export const clearTags = () => ({
- type: 'CLEAR_TAGS',
-});
diff --git a/client/talk-plugin-commentbox/constants.js b/client/talk-plugin-commentbox/constants.js
deleted file mode 100644
index 4a80bfe4f..000000000
--- a/client/talk-plugin-commentbox/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const ADD_TAG = 'ADD_TAG';
-export const REMOVE_TAG = 'REMOVE_TAG';
-export const CLEAR_TAGS = 'CLEAR_TAGS';
diff --git a/client/talk-plugin-commentbox/index.js b/client/talk-plugin-commentbox/index.js
deleted file mode 100644
index cbc56ade5..000000000
--- a/client/talk-plugin-commentbox/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as reducer } from './reducer';
diff --git a/client/talk-plugin-commentbox/reducer.js b/client/talk-plugin-commentbox/reducer.js
deleted file mode 100644
index 4c6e496ed..000000000
--- a/client/talk-plugin-commentbox/reducer.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { ADD_TAG, REMOVE_TAG, CLEAR_TAGS } from './constants';
-
-const initialState = {
- tags: [],
-};
-
-export default function commentBox(state = initialState, action) {
- switch (action.type) {
- case ADD_TAG:
- return {
- ...state,
- tags: [...state.tags, action.tag],
- };
- case REMOVE_TAG:
- return {
- ...state,
- tags: [
- ...state.tags.slice(0, action.idx),
- ...state.tags.slice(action.idx + 1),
- ],
- };
- case CLEAR_TAGS:
- return initialState;
- default:
- return state;
- }
-}
diff --git a/client/talk-plugin-flags/index.js b/client/talk-plugin-flags/index.js
deleted file mode 100644
index 8385ce482..000000000
--- a/client/talk-plugin-flags/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as FlagComment } from './components/FlagComment';
diff --git a/client/talk-plugin-moderation/index.js b/client/talk-plugin-moderation/index.js
deleted file mode 100644
index 38426167a..000000000
--- a/client/talk-plugin-moderation/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { default as ModerationLink } from './ModerationLink';
diff --git a/client/talk-plugin-replies/index.js b/client/talk-plugin-replies/index.js
deleted file mode 100644
index 813d96672..000000000
--- a/client/talk-plugin-replies/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import ReplyBox from './ReplyBox';
-import ReplyButton from './ReplyButton';
-
-export { ReplyBox, ReplyButton };
diff --git a/locales/en.yml b/locales/en.yml
index efcd7a1d9..14c29e3f6 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -65,7 +65,7 @@ en:
notify_approved: '{0} approved username {1}'
notify_rejected: '{0} rejected username {1}'
notify_flagged: '{0} reported username {1}'
- notify_changed: 'user {0} changed his username to {1}'
+ notify_changed: 'user {0} changed their username to {1}'
community:
account_creation_date: "Account Creation Date"
active: Active
@@ -254,6 +254,8 @@ en:
error: "Usernames can contain letters numbers and _ only"
label: "New Username"
msg: "Your account is currently suspended because your username has been deemed inappropriate. To restore your account please enter a new username. Please contact us if you have any questions."
+ changed_name:
+ msg: "Your username change is under review by our moderation team."
my_comments: "My Comments"
my_profile: "My profile"
new_count: "View {0} more {1}"
diff --git a/perms/reducers/mutation.js b/perms/reducers/mutation.js
index 3e73afbc9..73ee7ef28 100644
--- a/perms/reducers/mutation.js
+++ b/perms/reducers/mutation.js
@@ -15,7 +15,9 @@ module.exports = (user, perm) => {
case types.EDIT_COMMENT:
// Anyone can do these things if they aren't suspended, banned, or blocked
// as they're editing their username.
- return !['UNSET', 'REJECTED'].includes(user.status.username.status);
+ return !['UNSET', 'REJECTED', 'CHANGED'].includes(
+ user.status.username.status
+ );
case types.ADD_COMMENT_TAG:
case types.REMOVE_COMMENT_TAG:
diff --git a/plugin-api/alpha/client/actions/index.js b/plugin-api/alpha/client/actions/index.js
index 20bd2b9e3..9ef89f11b 100644
--- a/plugin-api/alpha/client/actions/index.js
+++ b/plugin-api/alpha/client/actions/index.js
@@ -1,5 +1,6 @@
-export { addTag, removeTag } from 'talk-plugin-commentbox/actions';
export {
addCommentClassName,
removeCommentClassName,
+ addCommentBoxTag as addTag,
+ removeCommentBoxTag as removeTag,
} from 'coral-embed-stream/src/actions/stream';
diff --git a/plugin-api/alpha/client/selectors/index.js b/plugin-api/alpha/client/selectors/index.js
index 988155308..214a07d0c 100644
--- a/plugin-api/alpha/client/selectors/index.js
+++ b/plugin-api/alpha/client/selectors/index.js
@@ -1,3 +1,3 @@
-export const commentBoxTagsSelector = state => state.commentBox.tags;
+export const commentBoxTagsSelector = state => state.stream.commentBoxTags;
export const commentClassNamesSelector = state =>
state.stream.commentClassNames;
diff --git a/plugins/talk-plugin-auth/client/components/FakeComment.js b/plugins/talk-plugin-auth/client/components/FakeComment.js
index d7367e838..82c61182b 100644
--- a/plugins/talk-plugin-auth/client/components/FakeComment.js
+++ b/plugins/talk-plugin-auth/client/components/FakeComment.js
@@ -1,6 +1,6 @@
import React from 'react';
import t from 'coral-framework/services/i18n';
-import { ReplyButton } from 'talk-plugin-replies';
+import ReplyButton from 'coral-embed-stream/src/tabs/stream/components/ReplyButton';
import styles from './FakeComment.css';
import { Icon } from 'plugin-api/beta/client/components/ui';
import { CommentTimestamp } from 'plugin-api/beta/client/components';
diff --git a/test/e2e/specs/04_userStatus.js b/test/e2e/specs/04_userStatus.js
index b63b051bb..8d659221d 100644
--- a/test/e2e/specs/04_userStatus.js
+++ b/test/e2e/specs/04_userStatus.js
@@ -15,13 +15,13 @@ module.exports = {
client.end();
},
- 'admin logs in': client => {
+ 'Admin logs in': client => {
const adminPage = client.page.admin();
const { testData: { admin } } = client.globals;
adminPage.navigateAndLogin(admin);
},
- 'admin flags users username as offensive': client => {
+ 'Admin flags users username as offensive': client => {
const embedStream = client.page.embedStream();
const comments = embedStream.navigate().ready();
@@ -42,7 +42,7 @@ module.exports = {
.waitForElementVisible('@popUpText')
.click('@continueButton');
},
- 'admin goes to Reported Usernames': client => {
+ 'Admin goes to Reported Usernames': client => {
const adminPage = client.page.admin();
const community = adminPage
@@ -54,14 +54,14 @@ module.exports = {
.waitForElementVisible('@flaggedAccountsContainer')
.waitForElementVisible('@flaggedUser');
},
- 'admin rejects the user flag': client => {
+ 'Admin rejects the user flag': client => {
const community = client.page.admin().section.community;
community
.waitForElementVisible('@flaggedUserRejectButton')
.click('@flaggedUserRejectButton');
},
- 'admin suspends the user': client => {
+ 'Admin suspends the user': client => {
const community = client.page.admin().section.community;
const usernameDialog = client.page.admin().section.usernameDialog;
@@ -76,7 +76,7 @@ module.exports = {
community.waitForElementNotPresent('@flaggedUser');
},
- 'admin logs out': client => {
+ 'Admin logs out': client => {
client.page.admin().logout();
},
'user logs in': client => {
@@ -114,6 +114,48 @@ module.exports = {
.click('@changeUsernameSubmitButton')
.waitForElementNotPresent('@changeUsernameInput');
},
+ 'user should not be able to comment still': client => {
+ const embedStream = client.page.embedStream();
+ const comments = embedStream.section.comments;
+
+ comments
+ .waitForElementNotPresent('@commentBoxTextarea')
+ .waitForElementNotPresent('@commentBoxPostButton');
+ },
+ 'user logs out': client => {
+ const embedStream = client.page.embedStream();
+ const comments = embedStream.section.comments;
+
+ comments.logout();
+ },
+ 'Admin accepts the user flag': client => {
+ const adminPage = client.page.admin();
+ const { testData: { admin } } = client.globals;
+
+ adminPage.navigateAndLogin(admin);
+
+ const community = adminPage
+ .navigate()
+ .ready()
+ .goToCommunity();
+
+ community
+ .waitForElementVisible('@flaggedAccountsContainer')
+ .waitForElementVisible('@flaggedUser')
+ .waitForElementVisible('@flaggedUserApproveButton')
+ .click('@flaggedUserApproveButton');
+
+ client.page.admin().logout();
+ },
+ 'user logs in to check comment': client => {
+ const { testData: { user } } = client.globals;
+ const embedStream = client.page.embedStream();
+
+ embedStream
+ .navigate()
+ .ready()
+ .openLoginPopup(popup => popup.login(user));
+ },
'user should be able to comment': client => {
const embedStream = client.page.embedStream();
const comments = embedStream.section.comments;
diff --git a/test/server/graph/mutations/createComment.js b/test/server/graph/mutations/createComment.js
index ffac0f510..fbbbf4d38 100644
--- a/test/server/graph/mutations/createComment.js
+++ b/test/server/graph/mutations/createComment.js
@@ -270,6 +270,49 @@ describe('graph.mutations.createComment', () => {
});
});
+ describe('user with different username statuses', () => {
+ beforeEach(() => AssetModel.create({ id: '123' }));
+
+ [
+ { status: 'UNSET', error: true },
+ { status: 'SET', error: false },
+ { status: 'APPROVED', error: false },
+ { status: 'REJECTED', error: true },
+ { status: 'CHANGED', error: true },
+ ].forEach(({ status, error }) => {
+ describe(`user.status.username.status=${status}`, () => {
+ it(`${error ? 'can not' : 'can'} create a comment`, async () => {
+ const context = new Context({
+ user: new UserModel({ status: { username: { status } } }),
+ });
+
+ const { data, errors } = await graphql(schema, query, {}, context);
+
+ if (errors) {
+ console.error(errors);
+ }
+ expect(errors).to.be.undefined;
+
+ if (error) {
+ expect(data.createComment).to.have.property('errors').not.null;
+ expect(data.createComment).to.have.property('comment').null;
+ expect(data.createComment.errors).to.have.length(1);
+ expect(data.createComment.errors[0]).to.have.property(
+ 'translation_key',
+ 'NOT_AUTHORIZED'
+ );
+ } else {
+ if (data.createComment.errors) {
+ console.error(data.createComment.errors);
+ }
+ expect(data.createComment).to.have.property('errors').null;
+ expect(data.createComment).to.have.property('comment').not.null;
+ }
+ });
+ });
+ });
+ });
+
describe('users with different roles', () => {
beforeEach(() => AssetModel.create({ id: '123' }));