diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js
index 2875d0397..eb30dc16c 100644
--- a/client/coral-embed-stream/src/components/Stream.js
+++ b/client/coral-embed-stream/src/components/Stream.js
@@ -1,28 +1,29 @@
import React, {PropTypes} from 'react';
+
import {Button} from 'coral-ui';
-import Comment from '../containers/Comment';
-import CommentBox from 'coral-plugin-commentbox/CommentBox';
-import SuspendedAccount from 'coral-framework/components/SuspendedAccount';
-import RestrictedContent from 'coral-framework/components/RestrictedContent';
-import ChangeUsernameContainer from 'coral-sign-in/containers/ChangeUsernameContainer';
-import IgnoredCommentTombstone from './IgnoredCommentTombstone';
-import InfoBox from 'coral-plugin-infobox/InfoBox';
-import QuestionBox from 'coral-plugin-questionbox/QuestionBox';
import LoadMore from './LoadMore';
import NewCount from './NewCount';
+import Comment from '../containers/Comment';
+import InfoBox from 'coral-plugin-infobox/InfoBox';
import {ModerationLink} from 'coral-plugin-moderation';
+import CommentBox from 'coral-plugin-commentbox/CommentBox';
+import QuestionBox from 'coral-plugin-questionbox/QuestionBox';
+import IgnoredCommentTombstone from './IgnoredCommentTombstone';
+import SuspendedAccount from 'coral-framework/components/SuspendedAccount';
+import RestrictedContent from 'coral-framework/components/RestrictedContent';
+import ChangeUsernameContainer
+ from 'coral-sign-in/containers/ChangeUsernameContainer';
class Stream extends React.Component {
-
- setActiveReplyBox = (reactKey) => {
+ setActiveReplyBox = reactKey => {
if (!this.props.auth.user) {
this.props.showSignInDialog();
} else {
this.props.setActiveReplyBox(reactKey);
}
- }
+ };
- render () {
+ render() {
const {
root: {asset, asset: {comments}, comment, myIgnoredUsers},
postItem,
@@ -39,152 +40,158 @@ class Stream extends React.Component {
ignoreUser,
auth: {loggedIn, isAdmin, user},
commentCountCache,
- editName,
+ editName
} = this.props;
const open = asset.closedAt === null;
// even though the permalinked comment is the highlighted one, we're displaying its parent + replies
- const highlightedComment = comment && comment.parent ? comment.parent : comment;
+ const highlightedComment = comment && comment.parent
+ ? comment.parent
+ : comment;
const banned = user && user.status === 'BANNED';
- const hasOlderComments = !!(
- asset &&
+ const hasOlderComments = !!(asset &&
asset.lastComment &&
- asset.lastComment.id !== asset.comments[asset.comments.length - 1].id
- );
+ asset.lastComment.id !== asset.comments[asset.comments.length - 1].id);
// 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 commentIsIgnored = (comment) => myIgnoredUsers && myIgnoredUsers.includes(comment.user.id);
+ const commentIsIgnored = comment =>
+ myIgnoredUsers && myIgnoredUsers.includes(comment.user.id);
return (
-
- {
- open
- ?
- :
{asset.settings.closedMessage}
- }
- {!loggedIn &&
Sign in to comment }
- {loggedIn && user &&
}
+
+ {open
+ ?
+ :
{asset.settings.closedMessage}
}
+ {!loggedIn &&
+
+ Sign in to comment
+ }
+ {loggedIn &&
+ user &&
+
}
{loggedIn &&
}
{/* the highlightedComment is isolated after the user followed a permalink */}
- {
- highlightedComment
+ {highlightedComment
?
- :
-
+ :
+
-
- {
- comments.map(comment =>
- commentIsIgnored(comment)
- ?
- :
- )
- }
-
-
-
- }
+
+ {comments.map(
+ comment =>
+ (commentIsIgnored(comment)
+ ?
+ : )
+ )}
+
+
+
}
);
}
@@ -201,7 +208,7 @@ Stream.propTypes = {
removeCommentTag: PropTypes.func,
// dispatch action to ignore another user
- ignoreUser: React.PropTypes.func,
+ ignoreUser: React.PropTypes.func
};
export default Stream;
diff --git a/client/coral-embed-stream/src/containers/Comment.js b/client/coral-embed-stream/src/containers/Comment.js
index 524f7ec62..9b48e0809 100644
--- a/client/coral-embed-stream/src/containers/Comment.js
+++ b/client/coral-embed-stream/src/containers/Comment.js
@@ -3,7 +3,15 @@ import Comment from '../components/Comment';
import withFragments from 'coral-framework/hocs/withFragments';
import {getSlotsFragments} from 'coral-framework/helpers/plugins';
-const pluginFragments = getSlotsFragments(['commentInfoBar', 'commentDetail']);
+const pluginFragments = getSlotsFragments([
+ 'streamQuestionArea',
+ 'commentInputArea',
+ 'commentInputDetailArea',
+ 'commentInfoBar',
+ 'commentActions',
+ 'commentContent',
+ 'commentReactions'
+]);
export default withFragments({
root: gql`
@@ -36,5 +44,5 @@ export default withFragments({
${pluginFragments.spreads('comment')}
}
${pluginFragments.definitions('comment')}
- `,
+ `
})(Comment);
diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js
index 12e05eed8..d5399fc60 100644
--- a/client/coral-embed-stream/src/containers/Embed.js
+++ b/client/coral-embed-stream/src/containers/Embed.js
@@ -3,6 +3,8 @@ import {compose, gql, graphql} from 'react-apollo';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import isEqual from 'lodash/isEqual';
+import branch from 'recompose/branch';
+import renderComponent from 'recompose/renderComponent';
import {Spinner} from 'coral-ui';
import {authActions, assetActions, pym} from 'coral-framework';
@@ -19,7 +21,6 @@ class EmbedContainer extends React.Component {
componentDidMount() {
pym.sendMessage('childReady');
- this.props.checkLogin();
}
componentWillReceiveProps(nextProps) {
@@ -108,6 +109,10 @@ const mapDispatchToProps = dispatch =>
export default compose(
connect(mapStateToProps, mapDispatchToProps),
+ branch(
+ props => !props.auth.checkedInitialLogin,
+ renderComponent(Spinner),
+ ),
withQuery,
)(EmbedContainer);
diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js
index 4df7fc960..49f234dab 100644
--- a/client/coral-embed-stream/src/containers/Stream.js
+++ b/client/coral-embed-stream/src/containers/Stream.js
@@ -19,24 +19,13 @@ const {showSignInDialog} = authActions;
const {addNotification} = notificationActions;
class StreamContainer extends React.Component {
- getCounts = ({asset_id, limit, sort}) => {
+ getCounts = (variables) => {
return this.props.data.fetchMore({
query: LOAD_COMMENT_COUNTS_QUERY,
- variables: {
- asset_id,
- limit,
- sort,
- excludeIgnored: this.props.data.variables.excludeIgnored,
- },
- updateQuery: (oldData, {fetchMoreResult:{asset}}) => {
- return {
- ...oldData,
- asset: {
- ...oldData.asset,
- commentCount: asset.commentCount
- }
- };
- }
+ variables,
+
+ // Apollo requires this, even though we don't use it...
+ updateQuery: data => data,
});
};
@@ -118,14 +107,11 @@ class StreamContainer extends React.Component {
};
componentDidMount() {
- this.props.data.refetch();
+ if (this.props.previousTab) {
+ this.props.data.refetch();
+ }
this.countPoll = setInterval(() => {
- const {asset} = this.props.root;
- this.getCounts({
- asset_id: asset.id,
- limit: asset.comments.length,
- sort: 'REVERSE_CHRONOLOGICAL'
- });
+ this.getCounts(this.props.data.variables);
}, NEW_COMMENT_COUNT_POLL_INTERVAL);
}
@@ -139,13 +125,13 @@ class StreamContainer extends React.Component {
}
const LOAD_COMMENT_COUNTS_QUERY = gql`
- query LoadCommentCounts($asset_id: ID, $limit: Int = 5, $sort: SORT_ORDER) {
- asset(id: $asset_id) {
+ query LoadCommentCounts($assetUrl: String, $assetId: ID, $excludeIgnored: Boolean) {
+ asset(id: $assetId, url: $assetUrl) {
id
- commentCount
- comments(sort: $sort, limit: $limit) {
+ commentCount(excludeIgnored: $excludeIgnored)
+ comments(limit: 10) {
id
- replyCount
+ replyCount(excludeIgnored: $excludeIgnored)
}
}
}
@@ -236,6 +222,7 @@ const mapStateToProps = state => ({
assetId: state.stream.assetId,
assetUrl: state.stream.assetUrl,
activeTab: state.embed.activeTab,
+ previousTab: state.embed.previousTab,
});
const mapDispatchToProps = dispatch =>
diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js
index f3f6075b6..2fd1e2731 100644
--- a/client/coral-embed-stream/src/index.js
+++ b/client/coral-embed-stream/src/index.js
@@ -3,6 +3,7 @@ import {render} from 'react-dom';
import {ApolloProvider} from 'react-apollo';
import {client} from 'coral-framework/services/client';
+import {checkLogin} from 'coral-framework/actions/auth';
import reducers from './reducers';
import localStore, {injectReducers} from 'coral-framework/services/store';
@@ -12,6 +13,11 @@ injectReducers(reducers);
const store = (window.opener && window.opener.coralStore) ? window.opener.coralStore : localStore;
+// Don't run this in the popup.
+if (store === localStore) {
+ store.dispatch(checkLogin());
+}
+
render(
diff --git a/client/coral-embed-stream/src/reducers/embed.js b/client/coral-embed-stream/src/reducers/embed.js
index e7fe5a533..0fd661543 100644
--- a/client/coral-embed-stream/src/reducers/embed.js
+++ b/client/coral-embed-stream/src/reducers/embed.js
@@ -2,6 +2,7 @@ import * as actions from '../constants/embed';
const initialState = {
activeTab: 'stream',
+ previousTab: '',
};
export default function stream(state = initialState, action) {
@@ -10,6 +11,7 @@ export default function stream(state = initialState, action) {
return {
...state,
activeTab: action.tab,
+ previousTab: state.activeTab,
};
default:
return state;
diff --git a/client/coral-framework/components/Slot.js b/client/coral-framework/components/Slot.js
index c1e5dfe65..8e2dd9502 100644
--- a/client/coral-framework/components/Slot.js
+++ b/client/coral-framework/components/Slot.js
@@ -1,20 +1,16 @@
-import React, {Component} from 'react';
-import {getSlotElements} from 'coral-framework/helpers/plugins';
+import React from 'react';
+import cn from 'classnames';
import styles from './Slot.css';
+import {getSlotElements} from 'coral-framework/helpers/plugins';
-class Slot extends Component {
- render() {
- const {fill, inline = false, ...rest} = this.props;
- return (
-
- {getSlotElements(fill, rest)}
-
- );
- }
+export default function Slot ({fill, inline = false, ...rest}) {
+ return (
+
+ {getSlotElements(fill, rest)}
+
+ );
}
Slot.propTypes = {
fill: React.PropTypes.string
};
-
-export default Slot;
diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js
index eb0b5386a..b1ea57ae1 100644
--- a/client/coral-framework/graphql/mutations/index.js
+++ b/client/coral-framework/graphql/mutations/index.js
@@ -57,7 +57,7 @@ export const postComment = graphql(POST_COMMENT, {
...oldData.asset,
comments: oldData.asset.comments.map((oldComment) => {
return oldComment.id === parent_id
- ? {...oldComment, replies: [...oldComment.replies, comment]}
+ ? {...oldComment, replies: [...oldComment.replies, comment], replyCount: oldComment.replyCount + 1}
: oldComment;
})
}
diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js
index df50fdcb7..20aee7abb 100644
--- a/client/coral-framework/helpers/plugins.js
+++ b/client/coral-framework/helpers/plugins.js
@@ -44,7 +44,7 @@ function getComponentFragments(components) {
* Returns an object that can be used to compose fragments or queries.
*
* Example:
- * const pluginFragments = getSlotsFragments(['commentInfoBar', 'commentDetail']);
+ * const pluginFragments = getSlotsFragments(['commentInfoBar', 'commentActions']);
* const rootFragment = gql`
* fragment Comment_root on RootQuery {
+ ${pluginFragments.spreads('root')}
@@ -65,10 +65,10 @@ export function getSlotsFragments(slots) {
const fragments = getComponentFragments(components);
return {
spreads(key) {
- return fragments[key] && fragments[key].spreads;
+ return (fragments[key] && fragments[key].spreads) || '';
},
definitions(key) {
- return fragments[key] && fragments[key].definitions;
+ return (fragments[key] && fragments[key].definitions) || '';
},
};
}
diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js
index 6e678cab9..83ea49ce5 100644
--- a/client/coral-framework/reducers/auth.js
+++ b/client/coral-framework/reducers/auth.js
@@ -8,6 +8,7 @@ const initialState = Map({
user: null,
showSignInDialog: false,
showCreateUsernameDialog: false,
+ checkedInitialLogin: false,
view: 'SIGNIN',
error: '',
passwordRequestSuccess: null,
@@ -71,10 +72,12 @@ export default function auth (state = initialState, action) {
.set('isLoading', true);
case actions.CHECK_LOGIN_FAILURE:
return state
+ .set('checkedInitialLogin', true)
.set('loggedIn', false)
.set('user', null);
case actions.CHECK_LOGIN_SUCCESS:
return state
+ .set('checkedInitialLogin', true)
.set('loggedIn', true)
.set('isAdmin', action.isAdmin)
.set('user', purge(action.user));
@@ -114,7 +117,11 @@ export default function auth (state = initialState, action) {
.set('isLoading', false)
.set('successSignUp', true);
case actions.LOGOUT_SUCCESS:
- return initialState;
+ return state
+ .set('user', null)
+ .set('isLoading', false)
+ .set('loggedIn', false)
+ .set('isAdmin', false);
case actions.INVALID_FORM:
return state
.set('error', action.error);
diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js
index 6dd2d486f..5d0906b6e 100644
--- a/client/coral-plugin-commentbox/CommentBox.js
+++ b/client/coral-plugin-commentbox/CommentBox.js
@@ -1,13 +1,13 @@
-import React, {Component, PropTypes} from 'react';
+import React, {PropTypes} from 'react';
+import {Button} from 'coral-ui';
+import {connect} from 'react-redux';
import {I18n} from '../coral-framework';
import translations from './translations.json';
-import {Button} from 'coral-ui';
import Slot from 'coral-framework/components/Slot';
-import {connect} from 'react-redux';
const name = 'coral-plugin-commentbox';
-class CommentBox extends Component {
+class CommentBox extends React.Component {
constructor(props) {
super(props);
@@ -151,13 +151,14 @@ class CommentBox extends Component {
id={isReply ? 'replyText' : 'commentText'}
onChange={this.handleChange}
rows={3}/>
+
maxCharCount ? `${name}-char-max` : ''}`}>
{maxCharCount && `${maxCharCount - length} ${lang.t('characters-remaining')}`}