Merge branch 'refactor-profile' into events

This commit is contained in:
Chi Vinh Le
2018-02-19 16:20:02 +01:00
9 changed files with 278 additions and 199 deletions
@@ -9,7 +9,7 @@ import AutomaticAssetClosure from '../containers/AutomaticAssetClosure';
import ExtendableTabPanel from '../containers/ExtendableTabPanel';
import { Tab, TabPane } from 'coral-ui';
import ProfileContainer from '../tabs/profile/containers/ProfileContainer';
import Profile from '../tabs/profile/containers/Profile';
import Popup from 'coral-framework/components/Popup';
import IfSlotIsNotEmpty from 'coral-framework/components/IfSlotIsNotEmpty';
import cn from 'classnames';
@@ -112,7 +112,7 @@ export default class Embed extends React.Component {
tabId="profile"
className="talk-embed-stream-profile-tab-pane"
>
<ProfileContainer />
<Profile />
</TabPane>,
<TabPane
key="config"
@@ -11,8 +11,18 @@ import { getTotalReactionsCount } from 'coral-framework/utils';
import t from 'coral-framework/services/i18n';
class Comment extends React.Component {
goToStory = () => {
this.props.navigate(this.props.comment.asset.url);
};
goToConversation = () => {
this.props.navigate(
`${this.props.comment.asset.url}?commentId=${this.props.comment.id}`
);
};
render() {
const { comment, link, data, root } = this.props;
const { comment, data, root } = this.props;
const reactionCount = getTotalReactionsCount(comment.action_summaries);
const queryData = { root, comment, asset: comment.asset };
@@ -67,7 +77,7 @@ class Comment extends React.Component {
<a
className={cn(styles.assetURL, 'my-comment-anchor')}
href="#"
onClick={link(`${comment.asset.url}`)}
onClick={this.goToStory}
>
{t('common.story')}:{' '}
{comment.asset.title ? comment.asset.title : comment.asset.url}
@@ -77,10 +87,7 @@ class Comment extends React.Component {
<div className={styles.sidebar}>
<ul>
<li>
<a
onClick={link(`${comment.asset.url}?commentId=${comment.id}`)}
className={styles.viewLink}
>
<a onClick={this.goToConversation} className={styles.viewLink}>
<Icon name="open_in_new" className={styles.iconView} />
{t('view_conversation')}
</a>
@@ -105,10 +112,10 @@ class Comment extends React.Component {
}
Comment.propTypes = {
comment: PropTypes.shape({
id: PropTypes.string,
body: PropTypes.string,
}).isRequired,
comment: PropTypes.object.isRequired,
navigate: PropTypes.func.isRequired,
data: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
};
export default Comment;
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import Comment from './Comment';
import Comment from '../containers/Comment';
import LoadMore from './LoadMore';
class CommentHistory extends React.Component {
@@ -21,7 +21,7 @@ class CommentHistory extends React.Component {
};
render() {
const { link, comments, data, root } = this.props;
const { navigate, comments, data, root } = this.props;
return (
<div>
<div className="commentHistory__list">
@@ -32,7 +32,7 @@ class CommentHistory extends React.Component {
data={data}
root={root}
comment={comment}
link={link}
navigate={navigate}
/>
);
})}
@@ -51,7 +51,7 @@ class CommentHistory extends React.Component {
CommentHistory.propTypes = {
comments: PropTypes.object.isRequired,
loadMore: PropTypes.func,
link: PropTypes.func,
navigate: PropTypes.func,
data: PropTypes.object,
root: PropTypes.object,
};
@@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import Slot from 'coral-framework/components/Slot';
import CommentHistory from '../containers/CommentHistory';
import t from 'coral-framework/services/i18n';
const Profile = ({ username, emailAddress, data, root }) => (
<div className="talk-my-profile talk-embed-stream-profile-container">
<h2>{username}</h2>
{emailAddress ? <p>{emailAddress}</p> : null}
<Slot fill="profileSections" data={data} queryData={{ root }} />
<hr />
<h3>{t('framework.my_comments')}</h3>
<CommentHistory data={data} root={root} />
</div>
);
Profile.propTypes = {
username: PropTypes.string,
emailAddress: PropTypes.string,
data: PropTypes.object,
root: PropTypes.object,
};
export default Profile;
@@ -0,0 +1,32 @@
import { gql, compose } from 'react-apollo';
import Comment from '../components/Comment';
import { withFragments } from 'coral-framework/hocs';
import { getSlotFragmentSpreads } from 'coral-framework/utils';
const slots = ['commentContent', 'historyCommentTimestamp'];
const withCommentFragments = withFragments({
comment: gql`
fragment TalkEmbedStream_Comment_Fragment on Comment {
id
body
replyCount
action_summaries {
count
__typename
}
asset {
id
title
url
${getSlotFragmentSpreads(slots, 'asset')}
}
created_at
${getSlotFragmentSpreads(slots, 'comment')}
}
`,
});
const enhance = compose(withCommentFragments);
export default enhance(Comment);
@@ -0,0 +1,103 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose, gql } from 'react-apollo';
import CommentHistory from '../components/CommentHistory';
import Comment from './Comment';
import { withFragments } from 'coral-framework/hocs';
import { appendNewNodes } from 'plugin-api/beta/client/utils';
import update from 'immutability-helper';
import { getDefinitionName } from 'coral-framework/utils';
class CommentHistoryContainer extends Component {
navigate = url => {
this.context.pym.sendMessage('navigate', url);
};
loadMore = () => {
return this.props.data.fetchMore({
query: LOAD_MORE_QUERY,
variables: {
limit: 5,
cursor: this.props.root.me.comments.endCursor,
},
updateQuery: (previous, { fetchMoreResult: { me: { comments } } }) => {
const updated = update(previous, {
me: {
comments: {
nodes: {
$apply: nodes => appendNewNodes(nodes, comments.nodes),
},
hasNextPage: { $set: comments.hasNextPage },
endCursor: { $set: comments.endCursor },
},
},
});
return updated;
},
});
};
render() {
return (
<CommentHistory
comments={this.props.root.me.comments}
data={this.props.data}
root={this.props.root}
loadMore={this.loadMore}
navigate={this.navigate}
/>
);
}
}
CommentHistoryContainer.contextTypes = {
pym: PropTypes.object,
};
CommentHistoryContainer.propTypes = {
data: PropTypes.object,
root: PropTypes.object,
};
const LOAD_MORE_QUERY = gql`
query TalkEmbedStream_CommentHistory_LoadMoreComments($limit: Int, $cursor: Cursor) {
me {
comments(query: { limit: $limit, cursor: $cursor }) {
nodes {
...${getDefinitionName(Comment.fragments.comment)}
}
endCursor
hasNextPage
}
}
}
${Comment.fragments.comment}
`;
const withCommentHistoryFragments = withFragments({
root: gql`
fragment TalkEmbedStream_CommentHistory on RootQuery {
me {
comments(query: {limit: 10}) {
nodes {
...${getDefinitionName(Comment.fragments.comment)}
}
endCursor
hasNextPage
}
}
}
${Comment.fragments.comment}
`,
});
const mapStateToProps = state => ({
currentUser: state.auth.user,
});
export default compose(
connect(mapStateToProps, null),
withCommentHistoryFragments
)(CommentHistoryContainer);
@@ -0,0 +1,93 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose, gql } from 'react-apollo';
import { bindActionCreators } from 'redux';
import { withQuery } from 'coral-framework/hocs';
import NotLoggedIn from '../components/NotLoggedIn';
import { Spinner } from 'coral-ui';
import Profile from '../components/Profile';
import CommentHistory from './CommentHistory';
import { getDefinitionName } from 'coral-framework/utils';
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
import { getSlotFragmentSpreads } from 'coral-framework/utils';
class ProfileContainer extends Component {
componentWillReceiveProps(nextProps) {
if (!this.props.currentUser && nextProps.currentUser) {
// Refetch after login.
this.props.data.refetch();
}
}
render() {
const { currentUser, showSignInDialog, root, data } = this.props;
const { me } = this.props.root;
const loading = this.props.data.loading;
if (this.props.data.error) {
return <div>{this.props.data.error.message}</div>;
}
if (!currentUser) {
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
}
if (loading || !me) {
return <Spinner />;
}
const localProfile = currentUser.profiles.find(p => p.provider === 'local');
const emailAddress = localProfile && localProfile.id;
return (
<Profile
username={me.username}
emailAddress={emailAddress}
data={data}
root={root}
/>
);
}
}
ProfileContainer.propTypes = {
data: PropTypes.object,
root: PropTypes.object,
currentUser: PropTypes.object,
showSignInDialog: PropTypes.func,
};
const slots = ['profileSections'];
const withProfileQuery = withQuery(
gql`
query CoralEmbedStream_Profile {
me {
id
username
}
...${getDefinitionName(CommentHistory.fragments.root)}
${getSlotFragmentSpreads(slots, 'root')}
}
${CommentHistory.fragments.root}
`,
{
options: {
fetchPolicy: 'network-only',
},
}
);
const mapStateToProps = state => ({
currentUser: state.auth.user,
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ showSignInDialog }, dispatch);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withProfileQuery
)(ProfileContainer);
@@ -1,178 +0,0 @@
import { connect } from 'react-redux';
import { compose, gql } from 'react-apollo';
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { withQuery } from 'coral-framework/hocs';
import Slot from 'coral-framework/components/Slot';
import cn from 'classnames';
import { link } from 'coral-framework/services/pym';
import NotLoggedIn from '../components/NotLoggedIn';
import { Spinner } from 'coral-ui';
import CommentHistory from '../components/CommentHistory';
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
import { appendNewNodes } from 'plugin-api/beta/client/utils';
import update from 'immutability-helper';
import { getSlotFragmentSpreads } from 'coral-framework/utils';
import t from 'coral-framework/services/i18n';
class ProfileContainer extends Component {
componentWillReceiveProps(nextProps) {
if (!this.props.currentUser && nextProps.currentUser) {
// Refetch after login.
this.props.data.refetch();
}
}
loadMore = () => {
return this.props.data.fetchMore({
query: LOAD_MORE_QUERY,
variables: {
limit: 5,
cursor: this.props.root.me.comments.endCursor,
},
updateQuery: (previous, { fetchMoreResult: { me: { comments } } }) => {
const updated = update(previous, {
me: {
comments: {
nodes: {
$apply: nodes => appendNewNodes(nodes, comments.nodes),
},
hasNextPage: { $set: comments.hasNextPage },
endCursor: { $set: comments.endCursor },
},
},
});
return updated;
},
});
};
render() {
const { currentUser, showSignInDialog, root, data } = this.props;
const { me } = this.props.root;
const loading = this.props.data.loading;
if (this.props.data.error) {
return <div>{this.props.data.error.message}</div>;
}
if (!currentUser) {
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
}
if (loading || !me) {
return <Spinner />;
}
const localProfile = currentUser.profiles.find(p => p.provider === 'local');
const emailAddress = localProfile && localProfile.id;
return (
<div className="talk-my-profile talk-embed-stream-profile-container">
<h2>{me.username}</h2>
{emailAddress ? <p>{emailAddress}</p> : null}
<Slot fill="profileSections" data={data} queryData={{ root }} />
<hr />
<h3>{t('framework.my_comments')}</h3>
<div
className={cn('talk-my-profile-comment-history', {
'talk-my-profile-comment-history-no-comments': !me.comments.nodes
.length,
})}
>
{me.comments.nodes.length ? (
<CommentHistory
data={data}
root={root}
comments={me.comments}
link={link}
loadMore={this.loadMore}
/>
) : (
<p className="talk-my-profile-comment-history-no-comments-cta">
{t('user_no_comment')}
</p>
)}
</div>
</div>
);
}
}
const slots = [
'profileSections',
// TODO: These Slots should be included in `talk-plugin-history` instead.
'commentContent',
'historyCommentTimestamp',
];
const CommentFragment = gql`
fragment TalkSettings_CommentConnectionFragment on CommentConnection {
nodes {
id
body
replyCount
action_summaries {
count
__typename
}
asset {
id
title
url
${getSlotFragmentSpreads(slots, 'asset')}
}
created_at
${getSlotFragmentSpreads(slots, 'comment')}
}
endCursor
hasNextPage
}
`;
const LOAD_MORE_QUERY = gql`
query TalkSettings_LoadMoreComments($limit: Int, $cursor: Cursor) {
me {
comments(query: { limit: $limit, cursor: $cursor }) {
...TalkSettings_CommentConnectionFragment
}
}
}
${CommentFragment}
`;
const withProfileQuery = withQuery(
gql`
query CoralEmbedStream_Profile {
me {
id
username
comments(query: {limit: 10}) {
...TalkSettings_CommentConnectionFragment
}
}
${getSlotFragmentSpreads(slots, 'root')}
}
${CommentFragment}
`,
{
options: {
fetchPolicy: 'network-only',
},
}
);
const mapStateToProps = state => ({
currentUser: state.auth.user,
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ showSignInDialog }, dispatch);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withProfileQuery
)(ProfileContainer);
+1 -5
View File
@@ -1,9 +1,5 @@
import Pym from 'pym.js';
const pym = new Pym.Child({ polling: 100 });
export default pym;
export const link = url => e => {
e.preventDefault();
pym.sendMessage('navigate', url);
};
export default pym;