Merge branch 'master' of github.com:coralproject/talk into comment-component

This commit is contained in:
Belen Curcio
2017-03-01 11:08:46 -03:00
12 changed files with 133 additions and 55 deletions
+6 -1
View File
@@ -43,6 +43,7 @@ class Comment extends React.Component {
postLike: PropTypes.func.isRequired,
deleteAction: PropTypes.func.isRequired,
parentId: PropTypes.string,
highlighted: PropTypes.string,
addNotification: PropTypes.func.isRequired,
postItem: PropTypes.func.isRequired,
depth: PropTypes.number.isRequired,
@@ -87,6 +88,7 @@ class Comment extends React.Component {
addNotification,
showSignInDialog,
postLike,
highlighted,
postFlag,
postDontAgree,
loadMore,
@@ -98,10 +100,12 @@ class Comment extends React.Component {
const like = getActionSummary('LikeActionSummary', comment);
const flag = getActionSummary('FlagActionSummary', comment);
const dontagree = getActionSummary('DontAgreeActionSummary', comment);
let commentClass = parentId ? `reply ${styles.Reply}` : `comment ${styles.Comment}`;
commentClass += highlighted === comment.id ? ' highlighted-comment' : '';
return (
<div
className={parentId ? `reply ${styles.Reply}` : `comment ${styles.Comment}`}
className={commentClass}
id={`c_${comment.id}`}
style={{marginLeft: depth * 30}}>
<hr aria-hidden={true} />
@@ -163,6 +167,7 @@ class Comment extends React.Component {
postItem={postItem}
depth={depth + 1}
asset={asset}
highlighted={highlighted}
currentUser={currentUser}
postLike={postLike}
postFlag={postFlag}
+45 -19
View File
@@ -31,12 +31,13 @@ import ChangeUsernameContainer from '../../coral-sign-in/containers/ChangeUserna
import ProfileContainer from 'coral-settings/containers/ProfileContainer';
import RestrictedContent from 'coral-framework/components/RestrictedContent';
import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer';
import Comment from './Comment';
import LoadMore from './LoadMore';
import NewCount from './NewCount';
class Embed extends Component {
state = {activeTab: 0, showSignInDialog: false};
state = {activeTab: 0, showSignInDialog: false, activeReplyBox: ''};
changeTab = (tab) => {
@@ -59,23 +60,6 @@ class Embed extends Component {
componentDidMount () {
pym.sendMessage('childReady');
pym.onMessage('DOMContentLoaded', hash => {
const commentId = hash.replace('#', 'c_');
let count = 0;
const interval = setInterval(() => {
if (document.getElementById(commentId)) {
window.clearInterval(interval);
pym.scrollParentToChildEl(commentId);
}
if (++count > 100) { // ~10 seconds
// give up waiting for the comments to load.
// it would be weird for the page to jump after that long.
window.clearInterval(interval);
}
}, 100);
});
}
componentWillReceiveProps (nextProps) {
@@ -85,11 +69,29 @@ class Embed extends Component {
}
}
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(`c_${this.props.data.comment.id}`), 0);
}
}
setActiveReplyBox (reactKey) {
if (!this.props.currentUser) {
const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75;
this.props.showSignInDialog(offset);
} else {
this.setState({activeReplyBox: reactKey});
}
}
render () {
const {activeTab} = this.state;
const {closedAt, countCache = {}} = this.props.asset;
const {loading, asset, refetch} = this.props.data;
const {loading, asset, refetch, comment} = this.props.data;
const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth;
const highlightedComment = comment && comment.parent ? comment.parent : comment;
const openStream = closedAt === null;
@@ -159,6 +161,28 @@ class Embed extends Component {
}
{!loggedIn && <SignInContainer requireEmailConfirmation={asset.settings.requireEmailConfirmation} offset={signInOffset}/>}
{loggedIn && user && <ChangeUsernameContainer loggedIn={loggedIn} offset={signInOffset} user={user} />}
{
highlightedComment &&
<Comment
refetch={refetch}
setActiveReplyBox={this.setActiveReplyBox}
activeReplyBox={this.state.activeReplyBox}
addNotification={addNotification}
depth={0}
postItem={this.props.postItem}
asset={asset}
currentUser={user}
highlighted={comment.id}
postLike={this.props.postLike}
postFlag={this.props.postFlag}
postDontAgree={this.props.postDontAgree}
loadMore={this.props.loadMore}
deleteAction={this.props.deleteAction}
showSignInDialog={this.props.showSignInDialog}
key={highlightedComment.id}
reactKey={highlightedComment.id}
comment={highlightedComment} />
}
<NewCount
commentCount={asset.commentCount}
countCache={countCache[asset.id]}
@@ -171,6 +195,8 @@ class Embed extends Component {
refetch={refetch}
addNotification={this.props.addNotification}
postItem={this.props.postItem}
setActiveReplyBox={this.setActiveReplyBox}
activeReplyBox={this.state.activeReplyBox}
asset={asset}
currentUser={user}
postLike={this.props.postLike}
+3 -16
View File
@@ -5,7 +5,6 @@ import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comment
class Stream extends React.Component {
static propTypes = {
refetch: PropTypes.func.isRequired,
addNotification: PropTypes.func.isRequired,
postItem: PropTypes.func.isRequired,
asset: PropTypes.object.isRequired,
@@ -19,7 +18,6 @@ class Stream extends React.Component {
constructor(props) {
super(props);
this.state = {activeReplyBox: '', countPoll: null};
this.setActiveReplyBox = this.setActiveReplyBox.bind(this);
}
componentDidMount() {
@@ -42,15 +40,6 @@ class Stream extends React.Component {
clearInterval(this.state.countPoll);
}
setActiveReplyBox (reactKey) {
if (!this.props.currentUser) {
const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75;
this.props.showSignInDialog(offset);
} else {
this.setState({activeReplyBox: reactKey});
}
}
render () {
const {
comments,
@@ -63,8 +52,7 @@ class Stream extends React.Component {
postDontAgree,
loadMore,
deleteAction,
showSignInDialog,
refetch
showSignInDialog
} = this.props;
return (
@@ -72,9 +60,8 @@ class Stream extends React.Component {
{
comments.map(comment =>
<Comment
refetch={refetch}
setActiveReplyBox={this.setActiveReplyBox}
activeReplyBox={this.state.activeReplyBox}
setActiveReplyBox={this.props.setActiveReplyBox}
activeReplyBox={this.props.activeReplyBox}
addNotification={addNotification}
depth={0}
postItem={postItem}
@@ -180,6 +180,11 @@ hr {
float: right;
}
.highlighted-comment {
padding-left: 10px;
border-left: 3px solid rgb(35,118,216);
}
/* Tag Labels */
.coral-plugin-tag-label {
+11 -6
View File
@@ -58,10 +58,11 @@
// ensure el has an id, as pym can't directly accept the HTMLElement
if ( ! el.id) {el.id = '_' + String(Math.random());}
var asset = opts.asset || window.location;
var asset = opts.asset || window.location.href.split('#')[0];
var comment = window.location.hash.slice(1);
var pymParent = new pym.Parent(
el.id,
buildStreamIframeUrl(opts.talk, asset),
buildStreamIframeUrl(opts.talk, asset, comment),
{
title: opts.title,
asset_url: asset,
@@ -76,14 +77,18 @@
return Coral;
// build the URL to load in the pym iframe
function buildStreamIframeUrl(talkBaseUrl, asset) {
var iframeUrl = [
function buildStreamIframeUrl(talkBaseUrl, asset, comment) {
var iframeArray = [
talkBaseUrl,
(talkBaseUrl.match(/\/$/) ? '' : '/'), // make sure no double-'/' if opts.talk already ends with '/'
'embed/stream?asset_url=',
encodeURIComponent(asset)
].join('');
return iframeUrl;
];
if (comment) {
iframeArray.push(`&comment_id=${comment}`);
}
return iframeArray.join('');
}
// Set up postMessage listeners/handlers on the pymParent
@@ -0,0 +1,13 @@
#import "../fragments/commentView.graphql"
query commentQuery($id: ID!) {
comment(id: $id) {
...commentView
parent {
...commentView
replies {
...commentView
}
}
}
}
@@ -87,7 +87,8 @@ export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}, n
export const queryStream = graphql(STREAM_QUERY, {
options: () => ({
variables: {
asset_url: getQueryVariable('asset_url')
asset_url: getQueryVariable('asset_url'),
comment_id: getQueryVariable('comment_id')
}
}),
props: ({data}) => ({
@@ -1,6 +1,15 @@
#import "../fragments/commentView.graphql"
query AssetQuery($asset_url: String!) {
query AssetQuery($asset_url: String!, $comment_id: ID!) {
comment(id: $comment_id) {
...commentView
parent {
...commentView
replies {
...commentView
}
}
}
asset(url: $asset_url) {
id
title
+22 -10
View File
@@ -39,7 +39,7 @@ const getCountsByAssetID = (context, asset_ids) => {
/**
* Returns the comment count for all comments that are public based on their
* parent ids.
* @param {Object} context graph context
*
* @param {Array<String>} parent_ids the ids of parents for which there are
* comments that we want to get
*/
@@ -271,18 +271,30 @@ const genRecentComments = (_, ids) => {
};
/**
* Returns the comment's by their id.
* genComments returns the comments by the id's. Only admins can see non-public comments.
* @param {Object} context graph context
* @param {Array<String>} ids the comment id's to fetch
* @return {Promise} resolves to the comments
*/
const genCommentsByID = (context, ids) => {
return CommentModel.find({
id: {
$in: ids
}
})
.then(util.singleJoinBy(ids, 'id'));
const genComments = ({user}, ids) => {
let comments;
if (user && user.hasRoles('ADMIN')) {
comments = CommentModel.find({
id: {
$in: ids
}
});
} else {
comments = CommentModel.find({
id: {
$in: ids
},
status: {
$in: ['NONE', 'ACCEPTED']
}
});
}
return comments.then(util.singleJoinBy(ids, 'id'));
};
/**
@@ -292,7 +304,7 @@ const genCommentsByID = (context, ids) => {
*/
module.exports = (context) => ({
Comments: {
get: new DataLoader((ids) => genCommentsByID(context, ids)),
get: new DataLoader((ids) => genComments(context, ids)),
getByQuery: (query) => getCommentsByQuery(context, query),
getCountByQuery: (query) => getCommentCountByQuery(context, query),
countByAssetID: new util.SharedCacheDataLoader('Comments.countByAssetID', 3600, (ids) => getCountsByAssetID(context, ids)),
+7
View File
@@ -1,4 +1,11 @@
const Comment = {
parent({parent_id}, _, {loaders: {Comments}}) {
if (parent_id == null) {
return null;
}
return Comments.get.load(parent_id);
},
user({author_id}, _, {loaders: {Users}}) {
return Users.getByID.load(author_id);
},
+3 -1
View File
@@ -38,7 +38,9 @@ const RootQuery = {
return Comments.getByQuery(query);
},
comment(_, {id}, {loaders: {Comments}}) {
return Comments.get.load(id);
},
commentCount(_, {query: {action_type, statuses, asset_id, parent_id}}, {user, loaders: {Actions, Comments}}) {
if (user == null || !user.hasRoles('ADMIN')) {
return null;
+6
View File
@@ -149,6 +149,9 @@ input CommentCountQuery {
# Comment is the base representation of user interaction in Talk.
type Comment {
# The parent of the comment (if there is one).
parent: Comment
# The ID of the comment.
id: ID!
@@ -477,6 +480,9 @@ type RootQuery {
# Site wide settings and defaults.
settings: Settings
# Finds a specific comment based on it's id.
comment(id: ID!): Comment
# All assets. Requires the `ADMIN` role.
assets: [Asset]