@@ -66,15 +66,17 @@ const Comment = ({actions = [], comment, ...props}) => {
{links ?
Contains Link : null}
- {actions.map((action, i) =>
-
props.acceptComment({commentId: comment.id})}
- rejectComment={() => props.rejectComment({commentId: comment.id})}
- />
- )}
+ {actions.map((action, i) => {
+ const active = (action === 'REJECT' && comment.status === 'REJECTED') ||
+ (action === 'APPROVE' && comment.status === 'ACCEPTED');
+ return props.acceptComment({commentId: comment.id})}
+ rejectComment={() => props.rejectComment({commentId: comment.id})} />;
+ })}
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js
index 14cf8ecfc..0517caf0f 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/CommentCount.js
@@ -1,9 +1,25 @@
import React, {PropTypes} from 'react';
import styles from './CommentCount.css';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from 'coral-admin/src/translations.json';
+const lang = new I18n(translations);
-const CommentCount = props => (
-
{props.count}
-);
+const CommentCount = ({count}) => {
+ let number = count;
+
+ // shorten large counts to abbreviations
+ if (number / 1e9 > 1) {
+ number = `${(number / 1e9).toFixed(1)}${lang.t('modqueue.billion')}`;
+ } else if (number / 1e6 > 1) {
+ number = `${(number / 1e6).toFixed(1)}${lang.t('modqueue.million')}`;
+ } else if (number / 1e3 > 1) {
+ number = `${(number / 1e3).toFixed(1)}${lang.t('modqueue.thousand')}`;
+ }
+
+ return (
+
{number}
+ );
+};
CommentCount.propTypes = {
count: PropTypes.number.isRequired
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
index 71b6b9e37..c90d3cd14 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/LoadMore.js
@@ -7,13 +7,16 @@ const LoadMore = ({comments, loadMore, sort, tab, assetId, showLoadMore}) =>
{
showLoadMore &&
- loadMore({
- cursor: comments[comments.length - 1].created_at,
+ onClick={() => {
+ const lastComment = comments[comments.length - 1];
+ const cursor = lastComment ? lastComment.created_at : null;
+ return loadMore({
+ cursor,
sort,
tab,
asset_id: assetId
- })}>
+ });
+ }}>
Load More
}
@@ -23,7 +26,7 @@ LoadMore.propTypes = {
comments: PropTypes.array.isRequired,
loadMore: PropTypes.func.isRequired,
sort: PropTypes.oneOf(['CHRONOLOGICAL', 'REVERSE_CHRONOLOGICAL']).isRequired,
- tab: PropTypes.oneOf(['rejected', 'premod', 'flagged', 'all']).isRequired,
+ tab: PropTypes.oneOf(['rejected', 'premod', 'flagged', 'all', 'accepted']).isRequired,
assetId: PropTypes.string,
showLoadMore: PropTypes.bool.isRequired
};
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
index 032aa740e..39594274e 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
@@ -10,7 +10,7 @@ import {Link} from 'react-router';
const lang = new I18n(translations);
const ModerationMenu = (
- {asset, allCount, premodCount, rejectedCount, flaggedCount, selectSort, sort}
+ {asset, allCount, acceptedCount, premodCount, rejectedCount, flaggedCount, selectSort, sort}
) => {
function getPath (type) {
@@ -28,6 +28,12 @@ const ModerationMenu = (
activeClassName={styles.active}>
{lang.t('modqueue.all')}
+
+
{lang.t('modqueue.approved')}
+
({
acceptComment: ({commentId}) => {
@@ -54,6 +55,21 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
},
updateQueries: {
ModQueue: (oldData) => {
+ const comment = views.reduce((comment, view) => {
+ return comment ? comment : oldData[view].find(c => c.id === commentId);
+ }, null);
+ let accepted;
+ let acceptedCount = oldData.acceptedCount;
+
+ // if the comment was already in the Approved queue, don't re-add it
+ if (comment.status === 'ACCEPTED') {
+ accepted = [...oldData.accepted];
+ } else {
+ comment.status = 'ACCEPTED';
+ acceptedCount++;
+ accepted = [comment, ...oldData.accepted];
+ }
+
const premod = oldData.premod.filter(c => c.id !== commentId);
const flagged = oldData.flagged.filter(c => c.id !== commentId);
const rejected = oldData.rejected.filter(c => c.id !== commentId);
@@ -63,11 +79,13 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
return {
...oldData,
- premodCount,
- flaggedCount,
- rejectedCount,
+ premodCount: Math.max(0, premodCount),
+ flaggedCount: Math.max(0, flaggedCount),
+ acceptedCount: Math.max(0, acceptedCount),
+ rejectedCount: Math.max(0, rejectedCount),
premod,
flagged,
+ accepted,
rejected,
};
}
@@ -82,21 +100,37 @@ export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
},
updateQueries: {
ModQueue: (oldData) => {
- const comment = oldData.premod.concat(oldData.flagged).filter(c => c.id === commentId)[0];
- const rejected = [comment].concat(oldData.rejected);
+ const comment = views.reduce((comment, view) => {
+ return comment ? comment : oldData[view].find(c => c.id === commentId);
+ }, null);
+ let rejected;
+ let rejectedCount = oldData.rejectedCount;
+
+ // if the item was already in the Rejected queue, don't put it in again
+ if (comment.status === 'REJECTED') {
+ rejected = oldData.rejected;
+ } else {
+ comment.status = 'REJECTED';
+ rejectedCount++;
+ rejected = [comment, ...oldData.rejected];
+ }
+
const premod = oldData.premod.filter(c => c.id !== commentId);
const flagged = oldData.flagged.filter(c => c.id !== commentId);
+ const accepted = oldData.accepted.filter(c => c.id !== commentId);
const premodCount = premod.length < oldData.premod.length ? oldData.premodCount - 1 : oldData.premodCount;
const flaggedCount = flagged.length < oldData.flagged.length ? oldData.flaggedCount - 1 : oldData.flaggedCount;
- const rejectedCount = oldData.rejectedCount + 1;
+ const acceptedCount = accepted.length < oldData.accepted.length ? oldData.acceptedCount - 1 : oldData.acceptedCount;
return {
...oldData,
- premodCount,
- flaggedCount,
- rejectedCount,
+ premodCount: Math.max(0, premodCount),
+ flaggedCount: Math.max(0, flaggedCount),
+ acceptedCount: Math.max(0, acceptedCount),
+ rejectedCount: Math.max(0, rejectedCount),
premod,
flagged,
+ accepted,
rejected
};
}
diff --git a/client/coral-admin/src/graphql/queries/getQueueCounts.graphql b/client/coral-admin/src/graphql/queries/getQueueCounts.graphql
new file mode 100644
index 000000000..4a7c00d87
--- /dev/null
+++ b/client/coral-admin/src/graphql/queries/getQueueCounts.graphql
@@ -0,0 +1,22 @@
+query Counts ($asset_id: ID) {
+ allCount: commentCount(query: {
+ asset_id: $asset_id
+ })
+ acceptedCount: commentCount(query: {
+ statuses: [ACCEPTED],
+ asset_id: $asset_id
+ })
+ premodCount: commentCount(query: {
+ statuses: [PREMOD],
+ asset_id: $asset_id
+ })
+ rejectedCount: commentCount(query: {
+ statuses: [REJECTED],
+ asset_id: $asset_id
+ })
+ flaggedCount: commentCount(query: {
+ action_type: FLAG,
+ asset_id: $asset_id,
+ statuses: [NONE, PREMOD]
+ })
+}
diff --git a/client/coral-admin/src/graphql/queries/index.js b/client/coral-admin/src/graphql/queries/index.js
index 53113558b..f26c8464e 100644
--- a/client/coral-admin/src/graphql/queries/index.js
+++ b/client/coral-admin/src/graphql/queries/index.js
@@ -4,6 +4,7 @@ import MOD_QUEUE_QUERY from './modQueueQuery.graphql';
import MOD_QUEUE_LOAD_MORE from './loadMore.graphql';
import MOD_USER_FLAGGED_QUERY from './modUserFlaggedQuery.graphql';
import METRICS from './metricsQuery.graphql';
+import GET_QUEUE_COUNTS from './getQueueCounts.graphql';
export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
options: ({params: {id = null}}) => {
@@ -33,31 +34,34 @@ export const getMetrics = graphql(METRICS, {
}
});
-export const loadMore = (fetchMore) => ({limit, cursor, sort, tab, asset_id}) => {
- let statuses;
+export const loadMore = (fetchMore) => ({limit = 10, cursor, sort, tab, asset_id}) => {
+ let variables = {
+ limit,
+ cursor,
+ sort,
+ asset_id
+ };
switch(tab) {
case 'all':
- statuses = null;
+ variables.statuses = null;
+ break;
+ case 'accepted':
+ variables.statuses = ['ACCEPTED'];
break;
case 'premod':
- statuses = ['PREMOD'];
+ variables.statuses = ['PREMOD'];
break;
case 'flagged':
- statuses = ['NONE', 'PREMOD'];
+ variables.statuses = ['NONE', 'PREMOD'];
+ variables.action_type = 'FLAG';
break;
case 'rejected':
- statuses = ['REJECTED'];
+ variables.statuses = ['REJECTED'];
break;
}
return fetchMore({
query: MOD_QUEUE_LOAD_MORE,
- variables: {
- limit,
- cursor,
- sort,
- statuses,
- asset_id
- },
+ variables,
updateQuery: (oldData, {fetchMoreResult:{comments}}) => {
return {
...oldData,
@@ -90,3 +94,14 @@ export const modQueueResort = (id, fetchMore) => (sort) => {
updateQuery: (oldData, {fetchMoreResult:{data}}) => data
});
};
+
+export const getQueueCounts = graphql(GET_QUEUE_COUNTS, {
+ options: ({params: {id = null}}) => {
+ return {
+ pollInterval: 5000,
+ variables: {
+ asset_id: id
+ }
+ };
+ }
+});
diff --git a/client/coral-admin/src/graphql/queries/loadMore.graphql b/client/coral-admin/src/graphql/queries/loadMore.graphql
index 56966a804..a42c7b2bf 100644
--- a/client/coral-admin/src/graphql/queries/loadMore.graphql
+++ b/client/coral-admin/src/graphql/queries/loadMore.graphql
@@ -1,7 +1,7 @@
#import "../fragments/commentView.graphql"
-query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!]) {
- comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort}) {
+query LoadMoreModQueue($limit: Int = 10, $cursor: Date, $sort: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) {
+ comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sort: $sort, action_type: $action_type}) {
...commentView
action_summaries {
count
diff --git a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql b/client/coral-admin/src/graphql/queries/modQueueQuery.graphql
index f124b87b5..80dec5ff7 100644
--- a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql
+++ b/client/coral-admin/src/graphql/queries/modQueueQuery.graphql
@@ -8,6 +8,13 @@ query ModQueue ($asset_id: ID, $sort: SORT_ORDER) {
}) {
...commentView
}
+ accepted: comments(query: {
+ statuses: [ACCEPTED],
+ asset_id: $asset_id,
+ sort: $sort
+ }) {
+ ...commentView
+ }
premod: comments(query: {
statuses: [PREMOD],
asset_id: $asset_id,
@@ -38,6 +45,10 @@ query ModQueue ($asset_id: ID, $sort: SORT_ORDER) {
allCount: commentCount(query: {
asset_id: $asset_id
})
+ acceptedCount: commentCount(query: {
+ statuses: [ACCEPTED],
+ asset_id: $asset_id
+ })
premodCount: commentCount(query: {
statuses: [PREMOD],
asset_id: $asset_id
diff --git a/client/coral-admin/src/services/fragmentMatcher.js b/client/coral-admin/src/services/fragmentMatcher.js
index daa01a538..531708f31 100644
--- a/client/coral-admin/src/services/fragmentMatcher.js
+++ b/client/coral-admin/src/services/fragmentMatcher.js
@@ -7,12 +7,37 @@ const fm = new IntrospectionFragmentMatcher({
introspectionQueryResultData: {
__schema: {
types: [
+ {
+ kind: 'INTERFACE',
+ name: 'UserError',
+ possibleTypes: [
+ {name: 'GenericUserError'},
+ {name: 'ValidationUserError'}
+ ]
+ },
+ {
+ kind: 'INTERFACE',
+ name: 'Response',
+ possibleTypes: [
+ {name: 'CreateCommentResponse'},
+ {name: 'CreateFlagResponse'},
+ {name: 'CreateDontAgreeResponse'},
+ {name: 'DeleteActionResponse'},
+ {name: 'SetUserStatusResponse'},
+ {name: 'SuspendUserResponse'},
+ {name: 'SetCommentStatusResponse'},
+ {name: 'AddCommentTagResponse'},
+ {name: 'RemoveCommentTagResponse'},
+ {name: 'IgnoreUserResponse'},
+ {name: 'StopIgnoringUserResponse'}
+ ]
+ },
{
kind: 'INTERFACE',
name: 'Action',
possibleTypes: [
+ {name: 'DefaultAction'},
{name: 'FlagAction'},
- {name: 'LikeAction'},
{name: 'DontAgreeAction'}
],
},
@@ -20,10 +45,18 @@ const fm = new IntrospectionFragmentMatcher({
kind: 'INTERFACE',
name: 'ActionSummary',
possibleTypes: [
+ {name: 'DefaultActionSummary'},
{name: 'FlagActionSummary'},
- {name: 'LikeActionSummary'},
{name: 'DontAgreeActionSummary'}
],
+ },
+ {
+ kind: 'INTERFACE',
+ name: 'AssetActionSummary',
+ possibleTypes: [
+ {name: 'DefaultAssetActionSummary'},
+ {name: 'FlagAssetActionSummary'},
+ ]
}
],
},
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 9e167232e..3f9392656 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -36,6 +36,7 @@
"modqueue": {
"likes": "likes",
"all": "all",
+ "approved": "approved",
"premod": "pre-mod",
"rejected": "rejected",
"flagged": "flagged",
@@ -62,7 +63,10 @@
"impersonating": "Impersonating",
"offensive": "Offensive",
"spam/ads": "Spam/Ads",
- "other": "Other"
+ "other": "Other",
+ "thousand": "k",
+ "million": "M",
+ "billion": "B"
},
"comment": {
"flagged": "flagged",
@@ -79,6 +83,8 @@
"copy": "Copy to Clipboard"
},
"configure": {
+ "sign-out": "Sign Out",
+ "shortcuts": "Shortcuts",
"closed-stream-settings": "Closed Stream Message",
"open-stream-configuration": "This comment stream is currently open. By closing this comment stream, no new comments may be submitted and all previous comments will still be displayed.",
"close-stream-configuration": "This comment stream is currently closed. By opening this comment stream, new comments may be submitted and displayed",
@@ -225,6 +231,8 @@
"loading": "Cargando resultados"
},
"modqueue": {
+ "all": "todos",
+ "approved": "aprobado",
"likes": "gustos",
"premod": "pre-mod",
"rejected": "rechazado",
@@ -243,7 +251,10 @@
"impersonating": "Suplantación",
"offensive": "Ofensivo",
"spam/ads": "Spam/Propaganda",
- "other": "Otros"
+ "other": "Otros",
+ "thousand": "m",
+ "million": "M",
+ "billion": "B"
},
"comment": {
"flagged": "marcado",
@@ -257,6 +268,8 @@
"username_flags": "marcas para este nombre de usuario"
},
"configure": {
+ "sign-out": "Desconectar",
+ "shortcuts": "Atajos",
"closed-stream-settings": "Mensaje a enviar cuando los comentarios están cerrados en el artículo",
"open-stream-configuration": "Este hilo de comentarios esta abierto. Al cerrarlo, ningún nuevo comentario será publicado y todos los comentarios anteriores serán mostrados.",
"close-stream-configuration": "Este hilo de comentario está en este momento cerrado. Al abrirlo, nuevos comentarios serán publicaods y mostrados.",
diff --git a/client/coral-embed-stream/src/AppRouter.js b/client/coral-embed-stream/src/AppRouter.js
index 133a3790d..e2553889c 100644
--- a/client/coral-embed-stream/src/AppRouter.js
+++ b/client/coral-embed-stream/src/AppRouter.js
@@ -1,7 +1,7 @@
import React from 'react';
import {Router, Route, browserHistory} from 'react-router';
-import Embed from './Embed';
+import Embed from './containers/Embed';
import SignInContainer from 'coral-sign-in/containers/SignInContainer';
const routes = (
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
deleted file mode 100644
index ba25b5b87..000000000
--- a/client/coral-embed-stream/src/Embed.js
+++ /dev/null
@@ -1,353 +0,0 @@
-import React from 'react';
-import {compose} from 'react-apollo';
-import {connect} from 'react-redux';
-import isEqual from 'lodash/isEqual';
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from 'coral-framework/translations';
-const lang = new I18n(translations);
-
-import {TabBar, Tab, TabContent, Spinner, Button} from 'coral-ui';
-
-const {logout, showSignInDialog, requestConfirmEmail, openSignInPopUp, checkLogin} = authActions;
-const {addNotification, clearNotification} = notificationActions;
-const {fetchAssetSuccess} = assetActions;
-import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments';
-
-import {queryStream} from 'coral-framework/graphql/queries';
-import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser, editComment} from 'coral-framework/graphql/mutations';
-import {editName} from 'coral-framework/actions/user';
-import {updateCountCache, viewAllComments} from 'coral-framework/actions/asset';
-import {notificationActions, authActions, assetActions, pym} from 'coral-framework';
-
-import Stream from './Stream';
-import InfoBox from 'coral-plugin-infobox/InfoBox';
-import QuestionBox from 'coral-plugin-questionbox/QuestionBox';
-import {ModerationLink} from 'coral-plugin-moderation';
-import Count from 'coral-plugin-comment-count/CommentCount';
-import CommentBox from 'coral-plugin-commentbox/CommentBox';
-import UserBox from 'coral-sign-in/components/UserBox';
-import SuspendedAccount from 'coral-framework/components/SuspendedAccount';
-import ChangeUsernameContainer from '../../coral-sign-in/containers/ChangeUsernameContainer';
-import ProfileContainer from 'coral-settings/containers/ProfileContainer';
-import RestrictedContent from 'coral-framework/components/RestrictedContent';
-import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer';
-import HighlightedComment from './Comment';
-import LoadMore from './LoadMore';
-import NewCount from './NewCount';
-
-class Embed extends React.Component {
-
- constructor(props) {
- super(props);
- this.state = {
- activeTab: 0,
- showSignInDialog: false,
- activeReplyBox: ''
- };
- }
-
- changeTab = (tab) => {
-
- // Everytime the comes from another tab, the Stream needs to be updated.
- if (tab === 0) {
- this.props.viewAllComments();
- this.props.data.refetch();
- }
-
- this.setState({
- activeTab: tab
- });
- }
-
- static propTypes = {
- data: React.PropTypes.shape({
- loading: React.PropTypes.bool,
- error: React.PropTypes.object
- }).isRequired,
-
- // dispatch action to add a tag to a comment
- addCommentTag: React.PropTypes.func,
-
- // dispatch action to remove a tag from a comment
- removeCommentTag: React.PropTypes.func,
-
- // dispatch action to ignore another user
- ignoreUser: React.PropTypes.func,
-
- // edit a comment, passed (id, { body })
- editComment: React.PropTypes.func,
- }
-
- componentDidMount () {
- pym.sendMessage('childReady');
- this.props.checkLogin();
- }
-
- componentWillUnmount () {
- clearInterval(this.state.countPoll);
- }
-
- componentWillReceiveProps (nextProps) {
- const {loadAsset} = this.props;
- if(!isEqual(nextProps.data.asset, this.props.data.asset)) {
- loadAsset(nextProps.data.asset);
-
- const {getCounts, updateCountCache, asset: {countCache}} = this.props;
- const {asset} = nextProps.data;
-
- if (!countCache) {
- updateCountCache(asset.id, asset.commentCount);
- }
-
- this.setState({
- countPoll: setInterval(() => {
- const {asset} = this.props.data;
- getCounts({
- asset_id: asset.id,
- limit: asset.comments.length,
- sort: 'REVERSE_CHRONOLOGICAL'
- });
- }, NEW_COMMENT_COUNT_POLL_INTERVAL)
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if(!isEqual(prevProps.data.comment, this.props.data.comment)) {
-
- // Scroll to a permalinked comment if one is in the URL once the page is done rendering.
- setTimeout(() => pym.scrollParentToChildEl('coralStream'), 0);
- }
- }
-
- setActiveReplyBox = (reactKey) => {
- if (!this.props.auth.user) {
- this.props.showSignInDialog();
- } else {
- this.setState({activeReplyBox: reactKey});
- }
- }
-
- render () {
- const {activeTab} = this.state;
- const {closedAt, countCache = {}} = this.props.asset;
- const {asset, refetch, comment} = this.props.data;
- const {loggedIn, isAdmin, user, showSignInDialog} = this.props.auth;
-
- // even though the permalinked comment is the highlighted one, we're displaying its parent + replies
- const highlightedComment = comment && comment.parent ? comment.parent : comment;
-
- const openStream = closedAt === null;
-
- const banned = user && user.status === 'BANNED';
-
- const hasOlderComments = !!(
- asset &&
- asset.lastComment &&
- asset.lastComment.id !== asset.comments[asset.comments.length - 1].id
- );
-
- const expandForLogin = showSignInDialog ? {
- minHeight: document.body.scrollHeight + 200
- } : {};
-
- if (!asset) {
- 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();
-
- const userBox =
this.props.logout().then(refetch)} changeTab={this.changeTab}/>;
-
- // TODO: This is a quickfix and will be replaced after our refactor.
- const ignoredUsers = this.props.userData.ignoredUsers;
- const commentIsIgnored = (comment) => ignoredUsers && ignoredUsers.includes(comment.user.id);
-
- return (
-
-
-
-
- {lang.t('myProfile')}
- Configure Stream
-
- {
- highlightedComment &&
-
{
- this.props.viewAllComments();
- this.props.data.refetch();
- }}>{lang.t('showAllComments')}
- }
-
- { loggedIn ? userBox : null }
- {
- openStream
- ?
- : {asset.settings.closedMessage}
- }
-
- {!loggedIn && Sign in to comment }
-
- {loggedIn && user && }
- {loggedIn && }
-
- {/* the highlightedComment is isolated after the user followed a permalink */}
- {
- highlightedComment
- ?
- :
- }
-
-
-
-
-
-
- { loggedIn ? userBox : null }
-
-
-
-
-
- );
- }
-}
-
-const mapStateToProps = state => ({
- auth: state.auth.toJS(),
- userData: state.user.toJS(),
- asset: state.asset.toJS(),
-});
-
-const mapDispatchToProps = dispatch => ({
- requestConfirmEmail: () => dispatch(requestConfirmEmail()),
- loadAsset: (asset) => dispatch(fetchAssetSuccess(asset)),
- addNotification: (type, text) => addNotification(type, text),
- clearNotification: () => dispatch(clearNotification()),
- editName: (username) => dispatch(editName(username)),
- showSignInDialog: () => dispatch(showSignInDialog()),
- updateCountCache: (id, count) => dispatch(updateCountCache(id, count)),
- viewAllComments: () => dispatch(viewAllComments()),
- logout: () => dispatch(logout()),
- openSignInPopUp: cb => dispatch(openSignInPopUp(cb)),
- checkLogin: () => dispatch(checkLogin()),
- dispatch: d => dispatch(d),
-});
-
-export default compose(
- connect(mapStateToProps, mapDispatchToProps),
- postComment,
- postFlag,
- postLike,
- postDontAgree,
- addCommentTag,
- removeCommentTag,
- ignoreUser,
- deleteAction,
- editComment,
- queryStream,
-)(Embed);
diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js
deleted file mode 100644
index a1b07f5ca..000000000
--- a/client/coral-embed-stream/src/Stream.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React, {PropTypes} from 'react';
-import Comment from './Comment';
-import IgnoredCommentTombstone from './IgnoredCommentTombstone';
-
-class Stream extends React.Component {
-
- static propTypes = {
- addNotification: PropTypes.func.isRequired,
- postItem: PropTypes.func.isRequired,
- asset: PropTypes.object.isRequired,
- open: PropTypes.bool.isRequired,
- comments: PropTypes.array.isRequired,
- currentUser: PropTypes.shape({
- username: PropTypes.string,
- id: PropTypes.string
- }),
-
- charCountEnable: PropTypes.bool.isRequired,
- maxCharCount: PropTypes.number,
-
- // dispatch action to add a tag to a comment
- addCommentTag: PropTypes.func,
-
- // dispatch action to remove a tag from a comment
- removeCommentTag: PropTypes.func,
-
- // dispatch action to ignore another user
- ignoreUser: React.PropTypes.func,
-
- // list of user ids that should be rendered as ignored
- ignoredUsers: React.PropTypes.arrayOf(React.PropTypes.string),
-
- // edit a comment, passed (id, { body })
- editComment: React.PropTypes.func,
- }
-
- constructor(props) {
- super(props);
- this.state = {activeReplyBox: '', countPoll: null};
- }
-
- render () {
- const {
- comments,
- currentUser,
- asset,
- postItem,
- addNotification,
- postFlag,
- postLike,
- open,
- postDontAgree,
- loadMore,
- deleteAction,
- showSignInDialog,
- addCommentTag,
- removeCommentTag,
- pluginProps,
- ignoreUser,
- ignoredUsers,
- charCountEnable,
- maxCharCount,
- } = this.props;
- const commentIsIgnored = (comment) => ignoredUsers && ignoredUsers.includes(comment.user.id);
- return (
-
- {
- comments.map(comment =>
- commentIsIgnored(comment)
- ?
- :
- )
- }
-
- );
- }
-}
-
-export default Stream;
diff --git a/client/coral-embed-stream/src/actions/embed.js b/client/coral-embed-stream/src/actions/embed.js
new file mode 100644
index 000000000..863494c68
--- /dev/null
+++ b/client/coral-embed-stream/src/actions/embed.js
@@ -0,0 +1,10 @@
+import * as actions from '../constants/embed';
+import {viewAllComments} from './stream';
+
+export const setActiveTab = (tab) => (dispatch, getState) => {
+ dispatch({type: actions.SET_ACTIVE_TAB, tab});
+ if (getState().stream.commentId) {
+ dispatch(viewAllComments());
+ }
+};
+
diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js
new file mode 100644
index 000000000..81b57a0fe
--- /dev/null
+++ b/client/coral-embed-stream/src/actions/stream.js
@@ -0,0 +1,40 @@
+import {pym} from 'coral-framework';
+import * as actions from '../constants/stream';
+
+export const setActiveReplyBox = (id) => ({type: actions.SET_ACTIVE_REPLY_BOX, id});
+export const setCommentCountCache = (amount) => ({type: actions.SET_COMMENT_COUNT_CACHE, amount});
+
+function removeParam(key, sourceURL) {
+ let rtn = sourceURL.split('?')[0];
+ let param;
+ let params_arr = [];
+ let queryString = (sourceURL.indexOf('?') !== -1) ? sourceURL.split('?')[1] : '';
+ if (queryString !== '') {
+ params_arr = queryString.split('&');
+ for (let i = params_arr.length - 1; i >= 0; i -= 1) {
+ param = params_arr[i].split('=')[0];
+ if (param === key) {
+ params_arr.splice(i, 1);
+ }
+ }
+ rtn = `${rtn}?${params_arr.join('&')}`;
+ }
+ return rtn;
+}
+
+export const viewAllComments = () => {
+
+ // remove the comment_id url param
+ const modifiedUrl = removeParam('comment_id', location.href);
+
+ try {
+
+ // "window" here refers to the embedded iframe
+ window.history.replaceState({}, document.title, modifiedUrl);
+
+ // also change the parent url
+ pym.sendMessage('coral-view-all-comments');
+ } catch (e) { /* not sure if we're worried about old browsers */ }
+
+ return {type: actions.VIEW_ALL_COMMENTS};
+};
diff --git a/client/coral-embed-stream/src/Comment.css b/client/coral-embed-stream/src/components/Comment.css
similarity index 100%
rename from client/coral-embed-stream/src/Comment.css
rename to client/coral-embed-stream/src/components/Comment.css
diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/components/Comment.js
similarity index 76%
rename from client/coral-embed-stream/src/Comment.js
rename to client/coral-embed-stream/src/components/Comment.js
index ef40c2a64..cda85f49d 100644
--- a/client/coral-embed-stream/src/Comment.js
+++ b/client/coral-embed-stream/src/components/Comment.js
@@ -1,11 +1,3 @@
-// this component will
-// render its children
-// render a like button
-// render a permalink button
-// render a reply button
-// render a flag button
-// translate things?
-
import React, {PropTypes} from 'react';
import PermalinkButton from 'coral-plugin-permalinks/PermalinkButton';
@@ -16,27 +8,35 @@ import Content from 'coral-plugin-commentcontent/CommentContent';
import PubDate from 'coral-plugin-pubdate/PubDate';
import {ReplyBox, ReplyButton} from 'coral-plugin-replies';
import FlagComment from 'coral-plugin-flags/FlagComment';
-import LikeButton from 'coral-plugin-likes/LikeButton';
-import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest, BestIndicator} from 'coral-plugin-best/BestButton';
-import LoadMore from 'coral-embed-stream/src/LoadMore';
+import {
+ BestButton,
+ IfUserCanModifyBest,
+ BEST_TAG,
+ commentIsBest,
+ BestIndicator
+} from 'coral-plugin-best/BestButton';
import Slot from 'coral-framework/components/Slot';
+import LoadMore from './LoadMore';
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import {TopRightMenu} from './TopRightMenu';
-import {getActionSummary, getTotalActionCount, iPerformedThisAction} from 'coral-framework/utils';
import classnames from 'classnames';
import {EditableCommentContent} from './EditableCommentContent';
+import {getActionSummary, iPerformedThisAction} from 'coral-framework/utils';
import styles from './Comment.css';
-const isStaff = (tags) => !tags.every((t) => t.name !== 'STAFF') ;
+const isStaff = tags => !tags.every(t => t.name !== 'STAFF');
-// hold actions links (e.g. Like, Reply) along the comment footer
+// hold actions links (e.g. Reply) along the comment footer
const ActionButton = ({children}) => {
- return { children } ;
+ return (
+
+ {children}
+
+ );
};
class Comment extends React.Component {
-
constructor(props) {
super(props);
@@ -60,7 +60,6 @@ class Comment extends React.Component {
setActiveReplyBox: PropTypes.func.isRequired,
showSignInDialog: PropTypes.func.isRequired,
postFlag: PropTypes.func.isRequired,
- postLike: PropTypes.func.isRequired,
deleteAction: PropTypes.func.isRequired,
parentId: PropTypes.string,
highlighted: PropTypes.string,
@@ -91,7 +90,8 @@ class Comment extends React.Component {
PropTypes.shape({
body: PropTypes.string.isRequired,
id: PropTypes.string.isRequired
- })),
+ })
+ ),
user: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired
@@ -155,7 +155,6 @@ class Comment extends React.Component {
postItem,
addNotification,
showSignInDialog,
- postLike,
highlighted,
postFlag,
postDontAgree,
@@ -169,12 +168,14 @@ class Comment extends React.Component {
disableReply,
commentIsIgnored,
maxCharCount,
- charCountEnable,
+ charCountEnable
} = this.props;
- const likeSummary = getActionSummary('LikeActionSummary', comment);
const flagSummary = getActionSummary('FlagActionSummary', comment);
- const dontAgreeSummary = getActionSummary('DontAgreeActionSummary', comment);
+ const dontAgreeSummary = getActionSummary(
+ 'DontAgreeActionSummary',
+ comment
+ );
let myFlag = null;
if (iPerformedThisAction('FlagActionSummary', comment)) {
myFlag = flagSummary.find(s => s.current_user);
@@ -182,46 +183,60 @@ class Comment extends React.Component {
myFlag = dontAgreeSummary.find(s => s.current_user);
}
- let commentClass = parentId ? `reply ${styles.Reply}` : `comment ${styles.Comment}`;
+ let commentClass = parentId
+ ? `reply ${styles.Reply}`
+ : `comment ${styles.Comment}`;
commentClass += comment.id === 'pending' ? ` ${styles.pendingComment}` : '';
// call a function, and if it errors, call addNotification('error', ...) (e.g. to show user a snackbar)
- const notifyOnError = (fn, errorToMessage) => async function (...args) {
- if (typeof errorToMessage !== 'function') {errorToMessage = (error) => error.message;}
- try {
- return await fn(...args);
- } catch (error) {
- addNotification('error', errorToMessage(error));
- throw error;
- }
- };
+ const notifyOnError = (fn, errorToMessage) =>
+ async function(...args) {
+ if (typeof errorToMessage !== 'function') {
+ errorToMessage = error => error.message;
+ }
+ try {
+ return await fn(...args);
+ } catch (error) {
+ addNotification('error', errorToMessage(error));
+ throw error;
+ }
+ };
- const addBestTag = notifyOnError(() => addCommentTag({
- id: comment.id,
- tag: BEST_TAG,
- }), () => 'Failed to tag comment as best');
+ const addBestTag = notifyOnError(
+ () =>
+ addCommentTag({
+ id: comment.id,
+ tag: BEST_TAG
+ }),
+ () => 'Failed to tag comment as best'
+ );
- const removeBestTag = notifyOnError(() => removeCommentTag({
- id: comment.id,
- tag: BEST_TAG,
- }), () => 'Failed to remove best comment tag');
+ const removeBestTag = notifyOnError(
+ () =>
+ removeCommentTag({
+ id: comment.id,
+ tag: BEST_TAG
+ }),
+ () => 'Failed to remove best comment tag'
+ );
return (