diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js
index bdb101362..c2f3a8056 100644
--- a/client/coral-admin/src/actions/auth.js
+++ b/client/coral-admin/src/actions/auth.js
@@ -9,7 +9,7 @@ const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error});
export const checkLogin = () => dispatch => {
dispatch(checkLoginRequest());
- coralApi('/auth')
+ return coralApi('/auth')
.then(result => {
const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length;
dispatch(checkLoginSuccess(result.user, isAdmin));
diff --git a/client/coral-admin/src/components/EmptyCard.js b/client/coral-admin/src/components/EmptyCard.js
new file mode 100644
index 000000000..42337a892
--- /dev/null
+++ b/client/coral-admin/src/components/EmptyCard.js
@@ -0,0 +1,14 @@
+import React, {PropTypes} from 'react';
+import {Card} from 'coral-ui';
+
+const EmptyCard = props => (
+
+ {props.children}
+
+);
+
+EmptyCard.propTypes = {
+ children: PropTypes.node.isRequired
+};
+
+export default EmptyCard;
diff --git a/client/coral-admin/src/containers/Community/Community.js b/client/coral-admin/src/containers/Community/Community.js
index bad5654cf..08a962c34 100644
--- a/client/coral-admin/src/containers/Community/Community.js
+++ b/client/coral-admin/src/containers/Community/Community.js
@@ -1,12 +1,12 @@
import React from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from '../../translations.json';
+import translations from 'coral-admin/src/translations.json';
import styles from './Community.css';
import Table from './Table';
import Loading from './Loading';
-import NoResults from './NoResults';
import {Pager} from 'coral-ui';
+import EmptyCard from '../../components/EmptyCard';
const lang = new I18n(translations);
@@ -54,13 +54,14 @@ const Community = ({isFetching, commenters, ...props}) => {
{ isFetching &&
}
- { !hasResults &&
}
- { hasResults &&
-
+ {
+ hasResults
+ ?
+ :
{lang.t('community.no-results')}
}
(
-
- No users found with that user name or email address
-
-);
-
-export default NoResults;
diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/LayoutContainer.js
index 225e92884..5ce208503 100644
--- a/client/coral-admin/src/containers/LayoutContainer.js
+++ b/client/coral-admin/src/containers/LayoutContainer.js
@@ -8,7 +8,11 @@ import {PermissionRequired} from '../components/PermissionRequired';
class LayoutContainer extends Component {
componentWillMount () {
const {checkLogin} = this.props;
- checkLogin();
+ checkLogin().then(() => {
+ if (!this.props.auth.isAdmin) {
+ location.href = '/admin/login';
+ }
+ });
}
render () {
const {isAdmin, loggedIn, loadingUser} = this.props.auth;
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
index d30f24c1f..0d48acc92 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
@@ -1,14 +1,21 @@
import React, {PropTypes} from 'react';
import Comment from './components/Comment';
+import EmptyCard from '../../components/EmptyCard';
import {actionsMap} from './helpers/moderationQueueActionsMap';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from 'coral-admin/src/translations';
+
+const lang = new I18n(translations);
const ModerationQueue = ({activeTab = 'premod', ...props}) => {
+ const areComments = props.data[activeTab].length;
return (
-
+
{
- props.data[activeTab].map((comment, i) => {
+ areComments
+ ? props.data[activeTab].map((comment, i) => {
const status = comment.action_summaries ? 'FLAGGED' : comment.status;
return {
currentAsset={props.currentAsset}
/>;
})
+ : {lang.t('modqueue.emptyqueue')}
}
@@ -29,7 +37,12 @@ const ModerationQueue = ({activeTab = 'premod', ...props}) => {
};
ModerationQueue.propTypes = {
- data: PropTypes.object.isRequired
+ data: PropTypes.object.isRequired,
+ acceptComment: PropTypes.func.isRequired,
+ rejectComment: PropTypes.func.isRequired,
+ showBanUserDialog: PropTypes.func.isRequired,
+ currentAsset: PropTypes.object,
+ suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired
};
export default ModerationQueue;
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
index f8f50c6df..b39eeb2f8 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {PropTypes} from 'react';
import timeago from 'timeago.js';
import Linkify from 'react-linkify';
import Highlighter from 'react-highlight-words';
@@ -17,7 +17,7 @@ const lang = new I18n(translations);
const Comment = ({actions = [], ...props}) => {
const links = linkify.getMatches(props.comment.body);
- const actionSumaries = props.comment.action_summaries;
+ const actionSummaries = props.comment.action_summaries;
return (
@@ -42,7 +42,7 @@ const Comment = ({actions = [], ...props}) => {
/>
)}
- {props.comment.user.banned === 'banned' ?
+ {props.comment.user.status === 'banned' ?
{lang.t('comment.banned_user')}
@@ -62,11 +62,31 @@ const Comment = ({actions = [], ...props}) => {
- {actionSumaries && }
+ {actionSummaries && }
);
};
+Comment.propTypes = {
+ acceptComment: PropTypes.func.isRequired,
+ rejectComment: PropTypes.func.isRequired,
+ suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
+ currentAsset: PropTypes.object,
+ isActive: PropTypes.bool.isRequired,
+ comment: PropTypes.shape({
+ body: PropTypes.string.isRequired,
+ action_summaries: PropTypes.array,
+ created_at: PropTypes.string.isRequired,
+ user: PropTypes.shape({
+ status: PropTypes.string
+ }),
+ asset: PropTypes.shape({
+ title: PropTypes.string,
+ id: PropTypes.string
+ })
+ })
+};
+
const linkStyles = {
backgroundColor: 'rgb(255, 219, 135)',
padding: '1px 2px'
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js
index bf5a34f29..6140abd90 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js
@@ -5,7 +5,7 @@ const FlagBox = props => (
Flags:
- {props.actionSumaries.map((action, i) =>
+ {props.actionSummaries.map((action, i) =>
{!action.reason ? No reason provided : action.reason} ({action.count} )
)}
@@ -13,7 +13,7 @@ const FlagBox = props => (
);
FlagBox.propTypes = {
- actionSumaries: PropTypes.array.isRequired
+ actionSummaries: PropTypes.array.isRequired
};
export default FlagBox;
diff --git a/client/coral-admin/src/containers/Streams/Streams.js b/client/coral-admin/src/containers/Streams/Streams.js
index 62f2f009e..96130fcba 100644
--- a/client/coral-admin/src/containers/Streams/Streams.js
+++ b/client/coral-admin/src/containers/Streams/Streams.js
@@ -8,6 +8,7 @@ import {Link} from 'react-router';
import {Pager, Icon} from 'coral-ui';
import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl';
+import EmptyCard from 'coral-admin/src/components/EmptyCard';
const limit = 25;
@@ -142,22 +143,25 @@ class Streams extends Component {
{lang.t('streams.oldest')}
-
-
- {lang.t('streams.article')}
-
- {lang.t('streams.pubdate')}
-
-
- {lang.t('streams.status')}
-
-
-
-
+ {
+ assetsIds.length
+ ?
+
+ {lang.t('streams.article')}
+
+ {lang.t('streams.pubdate')}
+
+
+ {lang.t('streams.status')}
+
+
+
+
+ : {lang.t('streams.empty_result')}
+ }
);
}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 5b15564d7..7219a22ca 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -7,7 +7,7 @@
"admin": "Administrator",
"moderator": "Moderator",
"role": "Select role...",
- "no-results": "No users found with that user name or email address.",
+ "no-results": "No users found with that user name or email address. They're hiding!",
"status": "Status",
"select-status": "Select status...",
"active": "Active",
@@ -32,6 +32,7 @@
"prevcomment": "Go to the previous comment",
"singleview": "Toggle single comment edit view",
"thismenu": "Open this menu",
+ "emptyqueue": "No more comments to moderate! You're all caught up. Go have some ☕️",
"showshortcuts": "Show Shortcuts"
},
"comment": {
@@ -113,6 +114,7 @@
"comment_count": "Comments"
},
"streams": {
+ "empty_result": "No assets match this search. Maybe try widening your search?",
"search": "Search",
"filter-streams": "Filter Streams",
"stream-status": "Stream Status",
@@ -152,6 +154,7 @@
"flagged": "marcado",
"shortcuts": "Atajos de teclado",
"close": "Cerrar",
+ "emptyqueue": "No se encontro ningún usuario. Están escondidos.",
"showshortcuts": "Mostrar atajos"
},
"comment": {
@@ -220,6 +223,7 @@
"comment_count": "Comentarios"
},
"streams": {
+ "empty_result": "No se encuentro articulo con esta busqueda. Tal vez extender la busqueda?",
"search": "",
"filter-streams": "",
"stream-status": "",
diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js
index 9d679473e..8371cec45 100644
--- a/client/coral-embed-stream/src/Comment.js
+++ b/client/coral-embed-stream/src/Comment.js
@@ -177,7 +177,7 @@ class Comment extends React.Component {
comment.replies &&
comment.replies.length}
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index a7c4dcb38..5e979d243 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -12,6 +12,7 @@ const {fetchAssetSuccess} = assetActions;
import {queryStream} from 'coral-framework/graphql/queries';
import {postComment, postFlag, postLike, deleteAction} from 'coral-framework/graphql/mutations';
import {editName} from 'coral-framework/actions/user';
+import {updateCountCache} from 'coral-framework/actions/asset';
import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework';
import Stream from './Stream';
@@ -28,6 +29,7 @@ import SettingsContainer from 'coral-settings/containers/SettingsContainer';
import RestrictedContent from 'coral-framework/components/RestrictedContent';
import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer';
import LoadMore from './LoadMore';
+import NewCount from './NewCount';
class Embed extends Component {
@@ -82,7 +84,7 @@ class Embed extends Component {
render () {
const {activeTab} = this.state;
- const {closedAt} = this.props.asset;
+ const {closedAt, countCache = {}} = this.props.asset;
const {loading, asset, refetch} = this.props.data;
const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth;
@@ -98,6 +100,11 @@ class Embed extends Component {
return ;
}
+ // Find the created_at date of the first comment. If no comments exist, set the date to a week ago.
+ const firstCommentDate = asset.comments[0]
+ ? asset.comments[0].created_at
+ : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString();
+
return (
@@ -132,6 +139,8 @@ class Embed extends Component {
postItem={this.props.postItem}
appendItemArray={this.props.appendItemArray}
updateItem={this.props.updateItem}
+ updateCountCache={this.props.updateCountCache}
+ countCache={countCache[asset.id]}
assetId={asset.id}
premod={asset.settings.moderation}
isReply={false}
@@ -147,6 +156,14 @@ class Embed extends Component {
}
{!loggedIn &&
}
{loggedIn && user && }
+
({
clearNotification: () => dispatch(clearNotification()),
editName: (username) => dispatch(editName(username)),
showSignInDialog: (offset) => dispatch(showSignInDialog(offset)),
+ updateCountCache: (id, count) => dispatch(updateCountCache(id, count)),
logout: () => dispatch(logout()),
dispatch: d => dispatch(d)
});
diff --git a/client/coral-embed-stream/src/LoadMore.js b/client/coral-embed-stream/src/LoadMore.js
index 89708272a..e33828bc1 100644
--- a/client/coral-embed-stream/src/LoadMore.js
+++ b/client/coral-embed-stream/src/LoadMore.js
@@ -12,7 +12,7 @@ const loadMoreComments = (assetId, comments, loadMore, parentId) => {
}
const cursor = parentId
- ? comments[1].created_at
+ ? comments[0].created_at
: comments[comments.length - 1].created_at;
loadMore({
diff --git a/client/coral-embed-stream/src/NewCount.js b/client/coral-embed-stream/src/NewCount.js
new file mode 100644
index 000000000..429ecc113
--- /dev/null
+++ b/client/coral-embed-stream/src/NewCount.js
@@ -0,0 +1,40 @@
+import React, {PropTypes} from 'react';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from 'coral-framework/translations.json';
+const lang = new I18n(translations);
+
+const onLoadMoreClick = ({loadMore, commentCount, firstCommentDate, assetId, updateCountCache}) => (e) => {
+ e.preventDefault();
+ updateCountCache(assetId, commentCount);
+ loadMore({
+ limit: 500,
+ cursor: firstCommentDate,
+ assetId,
+ sort: 'CHRONOLOGICAL'
+ }, true);
+};
+
+const NewCount = (props) => {
+ const newComments = props.commentCount - props.countCache;
+
+ return
+ {
+ props.countCache && newComments > 0 &&
+
+ {newComments === 1
+ ? lang.t('newCount', newComments, lang.t('comment'))
+ : lang.t('newCount', newComments, lang.t('comments'))}
+
+ }
+
;
+};
+
+NewCount.propTypes = {
+ commentCount: PropTypes.number.isRequired,
+ countCache: PropTypes.number,
+ loadMore: PropTypes.func.isRequired,
+ assetId: PropTypes.string.isRequired,
+ firstCommentDate: PropTypes.string.isRequired
+};
+
+export default NewCount;
diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js
index 2f0f4b01b..89a637ceb 100644
--- a/client/coral-embed-stream/src/Stream.js
+++ b/client/coral-embed-stream/src/Stream.js
@@ -1,5 +1,6 @@
import React, {PropTypes} from 'react';
import Comment from './Comment';
+import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments';
class Stream extends React.Component {
@@ -17,10 +18,30 @@ class Stream extends React.Component {
constructor(props) {
super(props);
- this.state = {activeReplyBox: ''};
+ this.state = {activeReplyBox: '', countPoll: null};
this.setActiveReplyBox = this.setActiveReplyBox.bind(this);
}
+ componentDidMount() {
+ const {asset, getCounts, updateCountCache} = this.props;
+
+ updateCountCache(asset.id, asset.commentCount);
+
+ // Note: Apollo's built-in polling doesn't work with fetchMore queries, so a
+ // setInterval is being used instead.
+ this.setState({
+ countPoll: setInterval(() => getCounts({
+ asset_id: asset.id,
+ limit: asset.comments.length,
+ sort: 'REVERSE_CHRONOLOGICAL'
+ }), NEW_COMMENT_COUNT_POLL_INTERVAL),
+ });
+ }
+
+ componentWillUnmount() {
+ clearInterval(this.state.countPoll);
+ }
+
setActiveReplyBox (reactKey) {
if (!this.props.currentUser) {
const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75;
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css
index 61965519b..52876ff79 100644
--- a/client/coral-embed-stream/style/default.css
+++ b/client/coral-embed-stream/style/default.css
@@ -359,18 +359,26 @@ button.coral-load-more {
text-align: center;
color: #FFF;
background-color: #2376D8;
+ cursor: pointer;
}
button.coral-load-more:hover {
background-color: #4399FF;
}
-.coral-load-more-replies {
+.coral-load-more-replies, .coral-new-comments {
width: 100%;
display: flex;
justify-content: center;
+ cursor: pointer;
}
-.coral-load-more-replies button.coral-load-more {
+.coral-new-comments {
+ position: relative;
+ top: 1.8em;
+ z-index: 100;
+}
+
+.coral-load-more-replies button.coral-load-more, .coral-new-comments button.coral-load-more{
width: initial;
}
diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js
index d5db289c3..02e72ea98 100644
--- a/client/coral-framework/actions/asset.js
+++ b/client/coral-framework/actions/asset.js
@@ -38,6 +38,7 @@ export const updateOpenStream = closedBody => (dispatch, getState) => {
const openStream = () => ({type: actions.OPEN_COMMENTS});
const closeStream = () => ({type: actions.CLOSE_COMMENTS});
+export const updateCountCache = (id, count) => ({type: actions.UPDATE_COUNT_CACHE, id, count});
export const updateOpenStatus = status => dispatch => {
if (status === 'open') {
diff --git a/client/coral-framework/constants/asset.js b/client/coral-framework/constants/asset.js
index 40f746706..234095d9d 100644
--- a/client/coral-framework/constants/asset.js
+++ b/client/coral-framework/constants/asset.js
@@ -8,3 +8,4 @@ export const UPDATE_ASSET_SETTINGS_FAILURE = 'UPDATE_ASSET_SETTINGS_FAILURE';
export const OPEN_COMMENTS = 'OPEN_COMMENTS';
export const CLOSE_COMMENTS = 'CLOSE_COMMENTS';
+export const UPDATE_COUNT_CACHE = 'UPDATE_COUNT_CACHE';
diff --git a/client/coral-framework/constants/comments.js b/client/coral-framework/constants/comments.js
index 17ea2223e..28db9cdf9 100644
--- a/client/coral-framework/constants/comments.js
+++ b/client/coral-framework/constants/comments.js
@@ -1 +1,2 @@
export const ADDTL_COMMENTS_ON_LOAD_MORE = 10;
+export const NEW_COMMENT_COUNT_POLL_INTERVAL = 20000;
diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js
index 09b3159d0..c993dfb6a 100644
--- a/client/coral-framework/graphql/queries/index.js
+++ b/client/coral-framework/graphql/queries/index.js
@@ -1,6 +1,7 @@
import {graphql} from 'react-apollo';
import STREAM_QUERY from './streamQuery.graphql';
import LOAD_MORE from './loadMore.graphql';
+import GET_COUNTS from './getCounts.graphql';
import MY_COMMENT_HISTORY from './myCommentHistory.graphql';
function getQueryVariable(variable) {
@@ -17,6 +18,72 @@ function getQueryVariable(variable) {
return 'http://localhost/default/stream';
}
+export const getCounts = (data) => ({asset_id, limit, sort}) => {
+ return data.fetchMore({
+ query: GET_COUNTS,
+ variables: {
+ asset_id,
+ limit,
+ sort
+ },
+ updateQuery: (oldData, {fetchMoreResult:{data}}) => {
+
+ return {
+ ...oldData,
+ asset: {
+ ...oldData.asset,
+ commentCount: data.asset.commentCount
+ }
+ };
+ }
+ });
+};
+
+export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}, newComments) => {
+ return data.fetchMore({
+ query: LOAD_MORE,
+ variables: {
+ limit,
+ cursor,
+ parent_id,
+ asset_id,
+ sort
+ },
+ updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => {
+
+ let updatedAsset;
+
+ if (parent_id) {
+
+ // If loading more replies
+ updatedAsset = {
+ ...oldData,
+ asset: {
+ ...oldData.asset,
+ comments: oldData.asset.comments.map((comment) =>
+ comment.id === parent_id
+ ? {...comment, replies: [...comment.replies, ...new_top_level_comments]}
+ : comment)
+ }
+ };
+ } else {
+
+ // If loading more top-level comments
+ updatedAsset = {
+ ...oldData,
+ asset: {
+ ...oldData.asset,
+ comments: newComments ? [...new_top_level_comments.reverse(), ...oldData.asset.comments]
+ : [...oldData.asset.comments, ...new_top_level_comments]
+ }
+ };
+ }
+
+ return updatedAsset;
+ }
+ });
+};
+
export const queryStream = graphql(STREAM_QUERY, {
options: () => ({
variables: {
@@ -25,40 +92,8 @@ export const queryStream = graphql(STREAM_QUERY, {
}),
props: ({data}) => ({
data,
- loadMore: ({limit, cursor, parent_id, asset_id, sort}) => {
- return data.fetchMore({
- query: LOAD_MORE,
- variables: {
- limit,
- cursor,
- parent_id,
- asset_id,
- sort
- },
- updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) =>
-
- // If loading more replies
- parent_id ? {
- ...oldData,
- asset: {
- ...oldData.asset,
- comments: oldData.asset.comments.map((comment) =>
- comment.id === parent_id
- ? {...comment, replies: [...comment.replies, ...new_top_level_comments]}
- : comment)
- }
- }
-
- // If loading more top-level comments
- : {
- ...oldData,
- asset: {
- ...oldData.asset,
- comments: [...oldData.asset.comments, ...new_top_level_comments]
- }
- }
- });
- }
+ loadMore: loadMore(data),
+ getCounts: getCounts(data),
})
});
diff --git a/client/coral-framework/reducers/asset.js b/client/coral-framework/reducers/asset.js
index f9d0a55e3..067edfc5b 100644
--- a/client/coral-framework/reducers/asset.js
+++ b/client/coral-framework/reducers/asset.js
@@ -19,6 +19,9 @@ export default function asset (state = initialState, action) {
case actions.UPDATE_ASSET_SETTINGS_SUCCESS:
return state
.setIn(['settings'], action.settings);
+ case actions.UPDATE_COUNT_CACHE:
+ return state
+ .setIn(['countCache', action.id], action.count);
default:
return state;
}
diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json
index 2884178b6..c817925eb 100644
--- a/client/coral-framework/translations.json
+++ b/client/coral-framework/translations.json
@@ -11,6 +11,9 @@
"button": "Submit",
"error": "Usernames can contain letters, numbers and _ only"
},
+ "newCount": "View {0} more {1}",
+ "comment": "comment",
+ "comments": "comments",
"error": {
"emailNotVerified": "Email address {0} not verified.",
"email": "Not a valid E-Mail",
@@ -39,6 +42,9 @@
"bannedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information",
"editNameMsg": "",
"loadMore": "Ver más",
+ "newCount": "Ver {0} {1} más",
+ "comment": "commentario",
+ "comments": "commentarios",
"error": {
"emailNotVerified": "Dirección de correo electrónico {0} no verificada.",
"email": "No es un email válido",
diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js
index 344ade150..3d895df07 100644
--- a/client/coral-plugin-commentbox/CommentBox.js
+++ b/client/coral-plugin-commentbox/CommentBox.js
@@ -29,6 +29,8 @@ class CommentBox extends Component {
commentPostedHandler,
postItem,
assetId,
+ updateCountCache,
+ countCache,
parentId,
addNotification,
authorId
@@ -44,14 +46,17 @@ class CommentBox extends Component {
if (this.props.charCount && this.state.body.length > this.props.charCount) {
return;
}
+ updateCountCache(assetId, countCache + 1);
postItem(comment, 'comments')
.then(({data}) => {
const postedComment = data.createComment.comment;
if (postedComment.status === 'REJECTED') {
addNotification('error', lang.t('comment-post-banned-word'));
+ updateCountCache(assetId, countCache);
} else if (postedComment.status === 'PREMOD') {
addNotification('success', lang.t('comment-post-notif-premod'));
+ updateCountCache(assetId, countCache);
} else {
addNotification('success', 'Your comment has been posted.');
}
diff --git a/client/coral-settings/translations.json b/client/coral-settings/translations.json
index 953708e61..c8398bd7b 100644
--- a/client/coral-settings/translations.json
+++ b/client/coral-settings/translations.json
@@ -1,6 +1,6 @@
{
"en":{
- "userNoComment": "This user has not yet left a comment.",
+ "userNoComment": "You've never left a comment. Join the conversation!",
"allComments": "All Comments",
"profileSettings": "Profile Settings",
"myCommentHistory": "My comment History",
@@ -9,7 +9,7 @@
"fromSettingsPage": "From the Settings Page you can see your comment history."
},
"es":{
- "userNoComment": "Aún no ha escrito ningún comentario.",
+ "userNoComment": "No has dejado áun ningún comentario. ¡Unete a la conversación!",
"allComments": "Todos los comentarios",
"profileSettings": "Configuración del perfil",
"myCommentHistory": "Mi historial de comentarios",
diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js
index 33d2c4f85..5112ba829 100644
--- a/graph/mutators/comment.js
+++ b/graph/mutators/comment.js
@@ -150,10 +150,8 @@ const createPublicComment = (context, commentInput) => {
item_id: comment.id,
item_type: 'COMMENTS',
action_type: 'FLAG',
- metadata: {
- field: 'body',
- details: 'Matched suspect word filters.'
- }
+ group_id: 'Matched suspect word filter',
+ metadata: {}
})
.then(() => comment);
}
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 72a54a0cd..7db4dbad8 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -448,7 +448,7 @@ type RootQuery {
# Comments returned based on a query.
comments(query: CommentsQuery!): [Comment]
- # Returne the count of comments satisfied by the query. Note that this edge is
+ # Return the count of comments satisfied by the query. Note that this edge is
# expensive as it is not batched. Requires the `ADMIN` role.
commentCount(query: CommentCountQuery!): Int
diff --git a/test/client/coral-framework/reducers/assetReducer.spec.js b/test/client/coral-framework/reducers/assetReducer.spec.js
new file mode 100644
index 000000000..c54f51737
--- /dev/null
+++ b/test/client/coral-framework/reducers/assetReducer.spec.js
@@ -0,0 +1,19 @@
+import {Map} from 'immutable';
+import {expect} from 'chai';
+import assetReducer from '../../../../client/coral-framework/reducers/asset';
+import * as actions from '../../../../client/coral-framework/constants/asset';
+
+describe ('coral-embed-stream assetReducer', () => {
+ describe('UPDATE_COUNT_CACHE', () => {
+ it('should update the count cache', () => {
+ const action = {
+ type: actions.UPDATE_COUNT_CACHE,
+ id: '123',
+ count: 456
+ };
+ const store = new Map({});
+ const result = assetReducer(store, action);
+ expect(result.getIn(['countCache', '123'])).to.equal(456);
+ });
+ });
+});
diff --git a/test/client/coral-framework/reducers/notificationReducer.spec.js b/test/client/coral-framework/reducers/notificationReducer.spec.js
new file mode 100644
index 000000000..40c50d11a
--- /dev/null
+++ b/test/client/coral-framework/reducers/notificationReducer.spec.js
@@ -0,0 +1,35 @@
+import {Map} from 'immutable';
+import {expect} from 'chai';
+import notificationReducer from '../../../../client/coral-framework/reducers/notification';
+import * as actions from '../../../../client/coral-framework/actions/notification';
+
+describe ('notificationsReducer', () => {
+ describe('ADD_NOTIFICATION', () => {
+ it('should add a notification', () => {
+ const action = {
+ type: actions.ADD_NOTIFICATION,
+ text: 'Test notification',
+ notifType: 'test'
+ };
+ const store = new Map({});
+ const result = notificationReducer(store, action);
+ expect(result.get('text')).to.equal(action.text);
+ expect(result.get('type')).to.equal(action.notifType);
+ });
+ });
+
+ describe('CLEAR_NOTIFICATION', () => {
+ it('should clear a notification', () => {
+ const action = {
+ type: actions.CLEAR_NOTIFICATION
+ };
+ const store = new Map({
+ text: 'Test notification',
+ type: 'test'
+ });
+ const result = notificationReducer(store, action);
+ expect(result.get('text')).to.equal('');
+ expect(result.get('type')).to.equal('');
+ });
+ });
+});