From f1899f2f4a0c63796851fd4a006a3981afff026a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 01:09:12 +0700 Subject: [PATCH 1/6] Threading support --- .../src/components/Comment.js | 5 +- .../src/components/Stream.js | 5 +- .../src/constants/stream.js | 1 + .../src/containers/Stream.js | 69 ++++++++++++------- .../coral-embed-stream/src/graphql/utils.js | 42 ++++++----- 5 files changed, 74 insertions(+), 48 deletions(-) diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index 2e854633b..5e357e9b7 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -9,6 +9,7 @@ import {can} from 'coral-framework/services/perms'; import {TransitionGroup} from 'react-transition-group'; import cn from 'classnames'; import styles from './Comment.css'; +import {THREADING_LEVEL} from '../constants/stream'; import { BestButton, @@ -316,6 +317,7 @@ export default class Comment extends React.Component { postDontAgree, setActiveReplyBox, activeReplyBox, + loadMore, deleteAction, disableReply, maxCharCount, @@ -553,7 +555,7 @@ export default class Comment extends React.Component { charCountEnable={charCountEnable} maxCharCount={maxCharCount} setActiveReplyBox={setActiveReplyBox} - parentId={parentId || comment.id} + parentId={(depth < THREADING_LEVEL) ? comment.id : parentId} addNotification={addNotification} postComment={postComment} currentUser={currentUser} @@ -583,6 +585,7 @@ export default class Comment extends React.Component { deleteAction={deleteAction} addTag={addTag} removeTag={removeTag} + loadMore={loadMore} ignoreUser={ignoreUser} charCountEnable={charCountEnable} maxCharCount={maxCharCount} diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index 0cfaf0368..4b04f5e7b 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -16,6 +16,7 @@ import IgnoredCommentTombstone from './IgnoredCommentTombstone'; import NewCount from './NewCount'; import {TransitionGroup} from 'react-transition-group'; import {forEachError} from 'coral-framework/utils'; +import {getTopLevelParent} from '../graphql/utils'; const hasComment = (nodes, id) => nodes.some((node) => node.id === id); @@ -169,9 +170,7 @@ class Stream extends React.Component { 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 && getTopLevelParent(comment); const banned = user && user.status === 'BANNED'; const temporarilySuspended = diff --git a/client/coral-embed-stream/src/constants/stream.js b/client/coral-embed-stream/src/constants/stream.js index 58004e877..5a8784a81 100644 --- a/client/coral-embed-stream/src/constants/stream.js +++ b/client/coral-embed-stream/src/constants/stream.js @@ -3,3 +3,4 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10; export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS'; export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME'; export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME'; +export const THREADING_LEVEL = 2; diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 3491fc846..28947b77e 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -2,7 +2,7 @@ import React from 'react'; import {gql, compose} from 'react-apollo'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {ADDTL_COMMENTS_ON_LOAD_MORE} from '../constants/stream'; +import {ADDTL_COMMENTS_ON_LOAD_MORE, THREADING_LEVEL} from '../constants/stream'; import { withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, withAddTag, withRemoveTag, withIgnoreUser, withEditComment, @@ -91,9 +91,7 @@ class StreamContainer extends React.Component { } loadNewReplies = (parent_id) => { - const comment = this.props.root.comment - ? this.props.root.comment.parent || this.props.root.comment // highlighted comment. - : this.props.root.asset.comments.nodes.filter((comment) => comment.id === parent_id)[0]; + const comment = findCommentInEmbedQuery(this.props.root, parent_id); return this.props.data.fetchMore({ query: LOAD_MORE_QUERY, @@ -153,20 +151,35 @@ class StreamContainer extends React.Component { } } +const nest = (def, level) => { + let result = ''; + for (let x = 0; x < level; x++) { + if (x === 0) { + result += def; + continue; + } + result = result.replace('...nest', def); + } + return result.replace('...nest', ''); +}; + const commentFragment = gql` fragment CoralEmbedStream_Stream_comment on Comment { id ...${getDefinitionName(Comment.fragments.comment)} - replyCount(excludeIgnored: $excludeIgnored) - replies(excludeIgnored: $excludeIgnored) { - nodes { - id - ...${getDefinitionName(Comment.fragments.comment)} + ${nest(` + replyCount(excludeIgnored: $excludeIgnored) + replies(excludeIgnored: $excludeIgnored) { + nodes { + id + ...${getDefinitionName(Comment.fragments.comment)} + ...nest + } + hasNextPage + startCursor + endCursor } - hasNextPage - startCursor - endCursor - } + `, THREADING_LEVEL)} } ${Comment.fragments.comment} `; @@ -205,16 +218,19 @@ const LOAD_MORE_QUERY = gql` nodes { id ...${getDefinitionName(Comment.fragments.comment)} - replyCount(excludeIgnored: $excludeIgnored) - replies(limit: 3, excludeIgnored: $excludeIgnored) { - nodes { - id - ...${getDefinitionName(Comment.fragments.comment)} + ${nest(` + replyCount(excludeIgnored: $excludeIgnored) + replies(limit: 3, excludeIgnored: $excludeIgnored) { + nodes { + id + ...${getDefinitionName(Comment.fragments.comment)} + ...nest + } + hasNextPage + startCursor + endCursor } - hasNextPage - startCursor - endCursor - } + `, THREADING_LEVEL)} } hasNextPage startCursor @@ -229,9 +245,12 @@ const fragments = { fragment CoralEmbedStream_Stream_root on RootQuery { comment(id: $commentId) @include(if: $hasComment) { ...CoralEmbedStream_Stream_comment - parent { - ...CoralEmbedStream_Stream_comment - } + ${nest(` + parent { + ...CoralEmbedStream_Stream_comment + ...nest + } + `, THREADING_LEVEL)} } asset(id: $assetId, url: $assetUrl) { id diff --git a/client/coral-embed-stream/src/graphql/utils.js b/client/coral-embed-stream/src/graphql/utils.js index eff1e2439..518f4b62f 100644 --- a/client/coral-embed-stream/src/graphql/utils.js +++ b/client/coral-embed-stream/src/graphql/utils.js @@ -1,21 +1,23 @@ import update from 'immutability-helper'; +import {THREADING_LEVEL} from '../constants/stream'; function applyToCommentsOrigin(root, callback) { if (root.comment) { - if (root.comment.parent) { - return update(root, { - comment: { - parent: { - $apply: (node) => callback(node), - }, - }, - }); + let comment = root.comment; + console.log(comment); + for (let depth = 0; depth <= THREADING_LEVEL; depth++) { + let changes = {$apply: (node) => node ? callback(node) : node}; + for (let i = 0; i < depth; i++) { + changes = {parent: changes}; + } + console.log(changes); + comment = update(comment, changes); } - return update(root, { - comment: { - $apply: (node) => callback(node), - }, - }); + + return { + ...root, + comment, + }; } return update(root, { asset: {$apply: (asset) => callback(asset)}, @@ -97,6 +99,13 @@ export function removeCommentFromEmbedQuery(root, id) { return applyToCommentsOrigin(root, (origin) => findAndRemoveComment(origin, id)); } +export function getTopLevelParent(comment) { + if (comment.parent) { + return getTopLevelParent(comment.parent); + } + return comment; +} + function findComment(nodes, callback) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; @@ -119,12 +128,7 @@ export function findCommentInEmbedQuery(root, callbackOrId) { callback = (node) => node.id === callbackOrId; } if (root.comment) { - if (callback(root.comment)) { - return root.comment; - } - if (root.comment.parent && callback(root.comment.parent)) { - return root.comment.parent; - } + return findComment([getTopLevelParent(root.comment)], callback); } if (!root.asset.comments) { return false; From 3a2a4379b8bfc61c055c095c4817f5a6f714a610 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 00:51:59 +0700 Subject: [PATCH 2/6] Change to 4 levels --- client/coral-embed-stream/src/constants/stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/constants/stream.js b/client/coral-embed-stream/src/constants/stream.js index 5a8784a81..2fe02d2ff 100644 --- a/client/coral-embed-stream/src/constants/stream.js +++ b/client/coral-embed-stream/src/constants/stream.js @@ -3,4 +3,4 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10; export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS'; export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME'; export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME'; -export const THREADING_LEVEL = 2; +export const THREADING_LEVEL = 3; From 31ab54d13c148404123514352d177ca6307ae4ac Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 21:32:33 +0700 Subject: [PATCH 3/6] First round styling --- .../src/components/Comment.css | 40 ++++++++++-- .../src/components/Comment.js | 65 +++++++++++-------- .../src/components/LoadMore.js | 18 +---- .../src/components/NewCount.js | 2 +- client/coral-embed-stream/style/default.css | 44 +++++++------ .../components/FlagButton.js | 20 +++--- .../coral-plugin-flags/components/styles.css | 7 ++ locales/en.yml | 6 +- locales/es.yml | 6 +- .../client/components/PermalinkButton.js | 8 +-- .../client/components/styles.css | 15 +++-- 11 files changed, 137 insertions(+), 94 deletions(-) create mode 100644 client/coral-plugin-flags/components/styles.css diff --git a/client/coral-embed-stream/src/components/Comment.css b/client/coral-embed-stream/src/components/Comment.css index 4ff0586da..4c8a9fc86 100644 --- a/client/coral-embed-stream/src/components/Comment.css +++ b/client/coral-embed-stream/src/components/Comment.css @@ -1,11 +1,41 @@ -.Reply { - position: relative; +.root { + margin-top: 16px; + margin-left: 20px; margin-bottom: 15px; + position: relative; } -.Comment { - margin-bottom: 15px; - position: relative; +.rootLevel0 { + margin-left: 0px; +} + +.comment { + padding-left: 20px; +} + +.commentLevel0 { + padding-left: 0px; +} + +.commentLevel1 { + border-left: 3px #212121 solid; +} + +.commentLevel2 { + border-left: 2px #6a6a6a solid; +} + +.commentLevel3 { + border-left: 2px #9e9e9e solid; +} + +.commentLevel4 { + border-left: 2px #c1c1c1 solid; +} + +.highlightedComment { + padding-left: 20px; + border-left: 3px solid rgb(35,118,216); } .pendingComment { diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index 5e357e9b7..b8b35e3fa 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -10,6 +10,8 @@ import {TransitionGroup} from 'react-transition-group'; import cn from 'classnames'; import styles from './Comment.css'; import {THREADING_LEVEL} from '../constants/stream'; +import merge from 'lodash/merge'; +import mapValues from 'lodash/mapValues'; import { BestButton, @@ -333,9 +335,12 @@ export default class Comment extends React.Component { const view = this.getVisibileReplies(); const {loadingState} = this.state; + const isReply = !!parentId; + const isPending = comment.id.indexOf('pending') >= 0; + const isHighlighted = highlighted === comment.id; const hasMoreComments = comment.replies && (comment.replies.hasNextPage || comment.replies.nodes.length > view.length); - const replyCount = this.hasIgnoredReplies() ? '' : comment.replyCount; + const moreRepliesCount = this.hasIgnoredReplies() ? -1 : comment.replyCount - view.length; const flagSummary = getActionSummary('FlagActionSummary', comment); const dontAgreeSummary = getActionSummary( 'DontAgreeActionSummary', @@ -348,11 +353,6 @@ export default class Comment extends React.Component { myFlag = dontAgreeSummary.find((s) => s.current_user); } - let commentClass = parentId - ? `reply ${styles.Reply}` - : `comment ${styles.Comment}`; - commentClass += comment.id.indexOf('pending') >= 0 ? ` ${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) { @@ -388,7 +388,7 @@ export default class Comment extends React.Component { ); /** - * classNamesToAdd + * conditionClassNames * adds classNames based on condition * classnames is an array of objects with key as classnames and value as conditions * i.e: @@ -398,34 +398,43 @@ export default class Comment extends React.Component { * * This will add myClassName to comments tagged with STAFF TAG. * **/ - - const classNamesToAdd = commentClassNames.reduce((acc, className) => { - let res = []; - - // Adding classNames based on tags - Object.keys(className).forEach((cn) => { - const condition = className[cn]; - condition.tags.forEach((tag) => { - if (hasTag(comment.tags, tag)) { - res = [...res, cn]; - } - }); + const conditionalClassNames = + mapValues(merge({}, ...commentClassNames), (condition) => { + if (condition.tags) { + return condition.tags.some((tag) => hasTag(comment.tags, tag)); + } + return false; }); - // TODO: Compare rest of the comment obj to the condition if needed - - return [...acc, ...res]; - }, []); + const rootClassNames = [ + 'talk-stream-comment-wrapper', + `talk-stream-comment-wrapper-level-${depth}`, + styles.root, + styles[`rootLevel${depth}`], + { + ...conditionalClassNames, + [styles.enter]: this.state.animateEnter, + }, + ]; return (
-
+ {!isReply &&
}
{isStaff(comment.tags) ? Staff : null} @@ -601,7 +610,7 @@ export default class Comment extends React.Component {
{ if (!count) { - return t('framework.view_all_replies_unknown_number'); + return t('framework.show_all_replies'); } if (count === 1) { - return t('framework.view_reply'); + return t('framework.show_1_more_reply'); } - if (this.initialState) { - return t('framework.view_all_replies_initial', count); - } else { - return t('framework.view_all_replies', count); - } - } - - componentWillReceiveProps(nextProps) { - if (['success', 'error'].indexOf(nextProps.loadingState) >= 0) { - this.initialState = false; - } + return t('framework.show_x_more_replies', count); } render () { diff --git a/client/coral-embed-stream/src/components/NewCount.js b/client/coral-embed-stream/src/components/NewCount.js index 1c135ab9b..8f3b77dcb 100644 --- a/client/coral-embed-stream/src/components/NewCount.js +++ b/client/coral-embed-stream/src/components/NewCount.js @@ -3,7 +3,7 @@ import React, {PropTypes} from 'react'; import t from 'coral-framework/services/i18n'; const NewCount = ({count, loadMore}) => { - return
+ return
{ count ? { @@ -214,12 +219,3 @@ export default class FlagButton extends Component { ); } } - -const styles = { - flaggedIcon: { - color: '#F00' - }, - unflaggedIcon: { - color: 'inherit' - } -}; diff --git a/client/coral-plugin-flags/components/styles.css b/client/coral-plugin-flags/components/styles.css new file mode 100644 index 000000000..6de9df387 --- /dev/null +++ b/client/coral-plugin-flags/components/styles.css @@ -0,0 +1,7 @@ +.button { + margin: 5px 0px 5px 10px; +} + +.flaggedIcon { + color: #f00 +} diff --git a/locales/en.yml b/locales/en.yml index dce6f621b..44db5e1b8 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -235,9 +235,9 @@ en: success_bio_update: "Your biography has been updated" success_name_update: "Your username has been updated" success_update_settings: "The changes you have made have been applied to the comment stream on this article" - view_all_replies_unknown_number: "view all replies" - view_all_replies: "view {0} replies" - view_all_replies_initial: "view all {0} replies" + show_all_replies: Show all replies + show_1_more_reply: Show 1 more reply + show_x_more_replies: Show {0} more replies view_more_comments: "view more comments" view_reply: "view reply" from_settings_page: "From the Profile Page you can see your comment history." diff --git a/locales/es.yml b/locales/es.yml index 6a0334fcd..a6b1bc4cd 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -232,9 +232,9 @@ es: success_bio_update: "Tu biografia fue actualizada" success_name_update: "Tu nombre de usuario ha sido actualizado" success_update_settings: "La configuración de este articulo fue actualizada" - view_all_replies_unknown_number: "ver todas las respuestas" - view_all_replies: "ver {0} respuestas" - view_all_replies_initial: "ver todas las {0} respuestas" + show_all_replies: "Mostrar todas las respuestas" + show_1_more_reply: "Mostrar 1 respuesta más" + show_x_more_replies: "Mostrar {0} respuestas más" view_more_comments: "Ver más comentarios" view_reply: "ver respuesta" from_settings_page: "Desde la página de configuración puedes ver tu historia de comentarios." diff --git a/plugins/talk-plugin-permalink/client/components/PermalinkButton.js b/plugins/talk-plugin-permalink/client/components/PermalinkButton.js index fc8561bdc..55b98e5d8 100644 --- a/plugins/talk-plugin-permalink/client/components/PermalinkButton.js +++ b/plugins/talk-plugin-permalink/client/components/PermalinkButton.js @@ -66,11 +66,11 @@ export default class PermalinkButton extends React.Component { return (
- + @@ -89,7 +89,7 @@ export default class PermalinkButton extends React.Component {
diff --git a/plugins/talk-plugin-permalink/client/components/styles.css b/plugins/talk-plugin-permalink/client/components/styles.css index 56d069755..f14e87881 100644 --- a/plugins/talk-plugin-permalink/client/components/styles.css +++ b/plugins/talk-plugin-permalink/client/components/styles.css @@ -42,7 +42,13 @@ } .button { + margin: 5px 0px 5px 10px; + cursor: pointer; +} + +.copyButton { display: inline-block; + margin: 5px 10px 5px 0px; float: right; box-sizing: border-box; margin: 0; @@ -52,18 +58,19 @@ height: auto; padding: 2px; transition: background-color 0.4s ease; + cursor: pointer; } -.button:hover{ +.copyButton:hover{ color: black; } -.button.success { +.copyButton.success { background-color: #00897B; color: white; } -.button.failure { +.copyButton.failure { background-color: #FF5252; color: white; } @@ -74,4 +81,4 @@ .active { display: block; -} \ No newline at end of file +} From 524be1b86993ded4c1bea69b6103e3f4cecb163c Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 22:10:40 +0700 Subject: [PATCH 4/6] Allow customizing thread levels via TALK_THREADING_LEVEL env --- client/coral-embed-stream/src/constants/stream.js | 2 +- webpack.config.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/coral-embed-stream/src/constants/stream.js b/client/coral-embed-stream/src/constants/stream.js index 2fe02d2ff..992767455 100644 --- a/client/coral-embed-stream/src/constants/stream.js +++ b/client/coral-embed-stream/src/constants/stream.js @@ -3,4 +3,4 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10; export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS'; export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME'; export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME'; -export const THREADING_LEVEL = 3; +export const THREADING_LEVEL = process.env.TALK_THREADING_LEVEL; diff --git a/webpack.config.js b/webpack.config.js index 130ced27b..c39726882 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -112,7 +112,8 @@ const config = { } }), new webpack.EnvironmentPlugin({ - 'TALK_PLUGINS_JSON': '{}' + 'TALK_PLUGINS_JSON': '{}', + 'TALK_THREADING_LEVEL': '3' }) ], resolveLoader: { From d155615102118f3f363d75c3d42a0482992e6be9 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 22:41:38 +0700 Subject: [PATCH 5/6] Add pending comment --- client/coral-embed-stream/src/components/Comment.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index b8b35e3fa..0e6201caf 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -432,6 +432,7 @@ export default class Comment extends React.Component { { [styles.pendingComment]: isPending, [styles.highlightedComment]: isHighlighted, + 'talk-stream-pending-comment': isPending, 'talk-stream-highlighted-comment': isHighlighted, } )} From 6d71bcbee96f359e04e7cb69c6fbe448d1aa8869 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 4 Jul 2017 22:56:58 +0700 Subject: [PATCH 6/6] Fix styling issues --- client/coral-embed-stream/style/default.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index b1d3a7f29..bd56cb748 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -450,7 +450,7 @@ button.comment__action-button[disabled], border-radius: 2px; } -.talk-load-more-replies .talk-load-more-button:hover { +.talk-load-more-replies .talk-load-more:hover button { background-color: #979797; color: white; }