diff --git a/client/coral-embed-stream/src/actions/embed.js b/client/coral-embed-stream/src/actions/embed.js index 09d68dbf1..863494c68 100644 --- a/client/coral-embed-stream/src/actions/embed.js +++ b/client/coral-embed-stream/src/actions/embed.js @@ -1,4 +1,10 @@ import * as actions from '../constants/embed'; +import {viewAllComments} from './stream'; -export const setActiveTab = (tab) => ({type: actions.SET_ACTIVE_TAB, tab}); +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/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index ee0afe4cc..b8346db0f 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -5,7 +5,7 @@ const lang = new I18n(translations); import {TabBar, Tab, TabContent, Button} from 'coral-ui'; -import Stream from './Stream'; +import Stream from '../containers/Stream'; import Count from 'coral-plugin-comment-count/CommentCount'; import UserBox from 'coral-sign-in/components/UserBox'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; @@ -17,75 +17,50 @@ export default class Embed extends React.Component { switch(tab) { case 0: this.props.setActiveTab('stream'); - this.props.data.refetch(); break; case 1: this.props.setActiveTab('profile'); + + // TODO: move data fetching to profile container. this.props.data.refetch(); break; case 2: this.props.setActiveTab('config'); + + // TODO: move data fetching to config container. this.props.data.refetch(); break; } } render () { - const {activeTab} = this.props; - const {asset, comment} = this.props.data; - const {loggedIn, isAdmin, user, showSignInDialog} = this.props.auth; + const {activeTab, logout, viewAllComments, commentId} = this.props; + const {asset: {totalCommentCount}} = this.props.data; + const {loggedIn, isAdmin, user} = this.props.auth; - const expandForLogin = showSignInDialog ? { - minHeight: document.body.scrollHeight + 200 - } : {}; - - const userBox = ; + const userBox = ; return ( -
+
- + {lang.t('myProfile')} Configure Stream { - comment && + commentId && } { loggedIn ? userBox : null } - u.id) : []} - auth={this.props.auth} - comment={this.props.data.comment} - commentCountCache={this.props.commentCountCache} - refetch={this.props.data.refetch} - editName={this.props.editName} - setCommentCountCache={this.props.setCommentCountCache} - /> + @@ -107,13 +82,4 @@ Embed.propTypes = { 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, }; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index 5b9fbdaa7..b4b23091c 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -24,8 +24,7 @@ class Stream extends React.Component { render () { const { - comments, - asset, + data: {asset, asset: {comments}, comment, myIgnoredUsers}, postItem, addNotification, postFlag, @@ -38,10 +37,7 @@ class Stream extends React.Component { removeCommentTag, pluginProps, ignoreUser, - ignoredUsers, auth: {loggedIn, isAdmin, user}, - comment, - refetch, commentCountCache, editName, } = this.props; @@ -62,7 +58,7 @@ class Stream extends React.Component { const firstCommentDate = asset.comments[0] ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); - const commentIsIgnored = (comment) => ignoredUsers && ignoredUsers.includes(comment.user.id); + const commentIsIgnored = (comment) => myIgnoredUsers && myIgnoredUsers.includes(comment.user.id); return (
{ @@ -94,7 +90,6 @@ class Stream extends React.Component { assetId={asset.id} premod={asset.settings.moderation} isReply={false} - currentUser={this.props.auth.user} authorId={user.id} charCount={asset.settings.charCountEnable && asset.settings.charCount} /> : null @@ -111,7 +106,6 @@ class Stream extends React.Component { { highlightedComment ? { - const {asset} = this.props.data; - getCounts({ - asset_id: asset.id, - limit: asset.comments.length, - sort: 'REVERSE_CHRONOLOGICAL' - }); - }, NEW_COMMENT_COUNT_POLL_INTERVAL); } } @@ -73,172 +59,20 @@ class EmbedContainer extends React.Component { } } -const fragments = { - commentView: gql` - fragment commentView on Comment { - id - body - created_at +const EMBED_QUERY = gql` + query EmbedQuery($assetId: ID, $assetUrl: String, $commentId: ID!, $hasComment: Boolean!, $excludeIgnored: Boolean) { + asset(id: $assetId, url: $assetUrl) { + totalCommentCount(excludeIgnored: $excludeIgnored) + } + me { status - tags { - name - } - user { - id - name: username - } - action_summaries { - ...actionSummaryView - } } - `, - actionSummaryView: gql` - fragment actionSummaryView on ActionSummary { - __typename - count - current_user { - id - created_at - } - } - `, -}; - -const LOAD_COMMENT_COUNTS_QUERY = gql` - query LoadCommentCounts($asset_id: ID, $limit: Int = 5, $sort: SORT_ORDER) { - asset(id: $asset_id) { - id - commentCount - comments(sort: $sort, limit: $limit) { - id - replyCount - } - } - } -`; - -const LOAD_MORE_QUERY = gql` - query LoadMoreComments($limit: Int = 5, $cursor: Date, $parent_id: ID, $asset_id: ID, $sort: SORT_ORDER, $excludeIgnored: Boolean) { - new_top_level_comments: comments(query: {limit: $limit, cursor: $cursor, parent_id: $parent_id, asset_id: $asset_id, sort: $sort, excludeIgnored: $excludeIgnored}) { - ...commentView - replyCount(excludeIgnored: $excludeIgnored) - replies(limit: 3) { - ...commentView - } - } - } - ${fragments.commentView} - ${fragments.actionSummaryView} -`; - -const STREAM_QUERY = gql` - query StreamQuery($assetId: ID, $assetUrl: String, $commentId: ID!, $hasComment: Boolean!, $excludeIgnored: Boolean) { - __typename ...Stream_root } ${Stream.fragments.root} `; -// get the counts of the top-level comments -const getCounts = (data) => ({asset_id, limit, sort}) => { - return data.fetchMore({ - query: LOAD_COMMENT_COUNTS_QUERY, - variables: { - asset_id, - limit, - sort, - excludeIgnored: data.variables.excludeIgnored, - }, - updateQuery: (oldData, {fetchMoreResult:{asset}}) => { - return { - ...oldData, - asset: { - ...oldData.asset, - commentCount: asset.commentCount - } - }; - } - }); -}; - -// handle paginated requests for more Comments pertaining to the Asset -const loadMore = (data) => ({limit, cursor, parent_id = null, asset_id, sort}, newComments) => { - return data.fetchMore({ - query: LOAD_MORE_QUERY, - variables: { - limit, // how many comments are we returning - cursor, // the date of the first/last comment depending on the sort order - parent_id, // if null, we're loading more top-level comments, if not, we're loading more replies to a comment - asset_id, // the id of the asset we're currently on - sort, // CHRONOLOGICAL or REVERSE_CHRONOLOGICAL - excludeIgnored: data.variables.excludeIgnored, - }, - updateQuery: (oldData, {fetchMoreResult:{new_top_level_comments}}) => { - let updatedAsset; - - if (!isNil(oldData.comment)) { // loaded replies on a highlighted (permalinked) comment - - let comment = {}; - if (oldData.comment && oldData.comment.parent) { - - // put comments (replies) onto the oldData.comment.parent object - // the initial comment permalinked was a reply - const uniqReplies = uniqBy([...new_top_level_comments, ...oldData.comment.parent.replies], 'id'); - comment.parent = {...oldData.comment.parent, replies: sortBy(uniqReplies, 'created_at')}; - } else if (oldData.comment) { - - // put the comments (replies) directly onto oldData.comment - // the initial comment permalinked was a top-level comment - const uniqReplies = uniqBy([...new_top_level_comments, ...oldData.comment.replies], 'id'); - comment.replies = sortBy(uniqReplies, 'created_at'); - } - - updatedAsset = { - ...oldData, - comment: { - ...oldData.comment, - ...comment - } - }; - - } else if (parent_id) { // If loading more replies - - updatedAsset = { - ...oldData, - asset: { - ...oldData.asset, - comments: oldData.asset.comments.map(comment => { - - // since the dipslayed replies and the returned replies can overlap, - // pull out the unique ones. - const uniqueReplies = uniqBy([...new_top_level_comments, ...comment.replies], 'id'); - - // since we just gave the returned replies precedence, they're now out of order. - // resort according to date. - return comment.id === parent_id - ? {...comment, replies: sortBy(uniqueReplies, 'created_at')} - : 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 withQuery = graphql(STREAM_QUERY, { +export const withQuery = graphql(EMBED_QUERY, { options: ({auth, commentId, assetId, assetUrl}) => ({ variables: { assetId, @@ -250,15 +84,12 @@ export const withQuery = graphql(STREAM_QUERY, { }), props: ({data}) => ({ data, - loadMore: loadMore(data), - getCounts: getCounts(data), }) }); const mapStateToProps = state => ({ auth: state.auth.toJS(), commentCountCache: state.stream.commentCountCache, - activeReplyBox: state.stream.activeReplyBox, commentId: state.stream.commentId, assetId: state.stream.assetId, assetUrl: state.stream.assetUrl, @@ -267,30 +98,16 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => bindActionCreators({ - showSignInDialog, - requestConfirmEmail, fetchAssetSuccess, - addNotification, - clearNotification, checkLogin, - editName, setCommentCountCache, viewAllComments, logout, - setActiveReplyBox, setActiveTab, }, dispatch); export default compose( connect(mapStateToProps, mapDispatchToProps), - postComment, - postFlag, - postLike, - postDontAgree, - addCommentTag, - removeCommentTag, - ignoreUser, - deleteAction, withQuery, )(EmbedContainer); diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index cecc6edc3..d57c37b33 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -1,9 +1,21 @@ import React from 'react'; -import {gql} from 'react-apollo'; +import {gql, compose} from 'react-apollo'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import uniqBy from 'lodash/uniqBy'; +import sortBy from 'lodash/sortBy'; +import isNil from 'lodash/isNil'; import Stream from '../components/Stream'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; +import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; +import {notificationActions, authActions} from 'coral-framework'; +import {editName} from 'coral-framework/actions/user'; +import {setCommentCountCache, setActiveReplyBox} from '../actions/stream'; -export default class StreamContainer extends React.Component { +const {showSignInDialog} = authActions; +const {addNotification} = notificationActions; + +class StreamContainer extends React.Component { getCounts = ({asset_id, limit, sort}) => { return this.props.data.fetchMore({ query: LOAD_COMMENT_COUNTS_QUERY, @@ -11,7 +23,7 @@ export default class StreamContainer extends React.Component { asset_id, limit, sort, - excludeIgnored: data.variables.excludeIgnored, + excludeIgnored: this.props.data.variables.excludeIgnored, }, updateQuery: (oldData, {fetchMoreResult:{asset}}) => { return { @@ -35,7 +47,7 @@ export default class StreamContainer extends React.Component { parent_id, // if null, we're loading more top-level comments, if not, we're loading more replies to a comment asset_id, // the id of the asset we're currently on sort, // CHRONOLOGICAL or REVERSE_CHRONOLOGICAL - excludeIgnored: data.variables.excludeIgnored, + excludeIgnored: this.props.data.variables.excludeIgnored, }, updateQuery: (oldData, {fetchMoreResult:{new_top_level_comments}}) => { let updatedAsset; @@ -103,9 +115,10 @@ export default class StreamContainer extends React.Component { }; componentDidMount() { + this.props.data.refetch(); this.countPoll = setInterval(() => { const {asset} = this.props.data; - this.props.getCounts({ + this.getCounts({ asset_id: asset.id, limit: asset.comments.length, sort: 'REVERSE_CHRONOLOGICAL' @@ -118,7 +131,7 @@ export default class StreamContainer extends React.Component { } render() { - return ; + return ; } } @@ -152,6 +165,33 @@ const actionSummaryViewFragment = gql` } `; +const LOAD_COMMENT_COUNTS_QUERY = gql` + query LoadCommentCounts($asset_id: ID, $limit: Int = 5, $sort: SORT_ORDER) { + asset(id: $asset_id) { + id + commentCount + comments(sort: $sort, limit: $limit) { + id + replyCount + } + } + } +`; + +const LOAD_MORE_QUERY = gql` + query LoadMoreComments($limit: Int = 5, $cursor: Date, $parent_id: ID, $asset_id: ID, $sort: SORT_ORDER, $excludeIgnored: Boolean) { + new_top_level_comments: comments(query: {limit: $limit, cursor: $cursor, parent_id: $parent_id, asset_id: $asset_id, sort: $sort, excludeIgnored: $excludeIgnored}) { + ...commentView + replyCount(excludeIgnored: $excludeIgnored) + replies(limit: 3) { + ...commentView + } + } + } + ${commentViewFragment} + ${actionSummaryViewFragment} +`; + StreamContainer.fragments = { root: gql` fragment Stream_root on RootQuery { @@ -205,8 +245,44 @@ StreamContainer.fragments = { id, username, } + me { + status + } } ${commentViewFragment} ${actionSummaryViewFragment} `, }; + +const mapStateToProps = state => ({ + auth: state.auth.toJS(), + commentCountCache: state.stream.commentCountCache, + activeReplyBox: state.stream.activeReplyBox, + + commentId: state.stream.commentId, + assetId: state.stream.assetId, + assetUrl: state.stream.assetUrl, + activeTab: state.embed.activeTab, +}); + +const mapDispatchToProps = dispatch => + bindActionCreators({ + showSignInDialog, + addNotification, + setActiveReplyBox, + editName, + setCommentCountCache, + }, dispatch); + +export default compose( + connect(mapStateToProps, mapDispatchToProps), + postComment, + postFlag, + postLike, + postDontAgree, + addCommentTag, + removeCommentTag, + ignoreUser, + deleteAction, +)(StreamContainer); + diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index ed0579c25..eb0b5386a 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -9,8 +9,6 @@ import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; -import MY_IGNORED_USERS from '../queries/myIgnoredUsers.graphql'; - import commentView from '../fragments/commentView.graphql'; export const postComment = graphql(POST_COMMENT, { @@ -43,7 +41,7 @@ export const postComment = graphql(POST_COMMENT, { } }, updateQueries: { - AssetQuery: (oldData, {mutationResult: {data: {createComment: {comment}}}}) => { + EmbedQuery: (oldData, {mutationResult: {data: {createComment: {comment}}}}) => { if (oldData.asset.settings.moderation === 'PRE' || comment.status === 'PREMOD' || comment.status === 'REJECTED') { return oldData; @@ -161,9 +159,9 @@ export const ignoreUser = graphql(IGNORE_USER, { variables: { id, }, - refetchQueries: [{ - query: MY_IGNORED_USERS, - }] + refetchQueries: [ + 'EmbedQuery', 'myIgnoredUsers', + ] }); }}), }); @@ -178,10 +176,7 @@ export const stopIgnoringUser = graphql(STOP_IGNORING_USER, { id, }, refetchQueries: [ - { - query: MY_IGNORED_USERS, - }, - 'StreamQuery', + 'EmbedQuery', 'myIgnoredUsers', ] }); } diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index 3db1c6ad0..c4e0df432 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -196,7 +196,6 @@ CommentBox.propTypes = { authorId: PropTypes.string.isRequired, isReply: PropTypes.bool.isRequired, canPost: PropTypes.bool, - currentUser: PropTypes.object }; const mapStateToProps = ({commentBox}) => ({commentBox});