From e24a0caa20fcd3c25ef12dac6e0459e2b91f3426 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 21 Feb 2017 12:08:02 -0700 Subject: [PATCH 01/16] add EmptyCard component. fix typos. more PropTypes. --- .../coral-admin/src/components/EmptyCard.js | 14 ++++++++++ .../ModerationQueue/ModerationQueue.js | 15 ++++++++--- .../ModerationQueue/components/Comment.js | 26 ++++++++++++++++--- .../ModerationQueue/components/FlagBox.js | 4 +-- 4 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 client/coral-admin/src/components/EmptyCard.js diff --git a/client/coral-admin/src/components/EmptyCard.js b/client/coral-admin/src/components/EmptyCard.js new file mode 100644 index 000000000..e0f837e69 --- /dev/null +++ b/client/coral-admin/src/components/EmptyCard.js @@ -0,0 +1,14 @@ +import React, {PropTypes} from 'react'; +import {Card} from 'coral-ui'; + +const EmptyCard = props => ( + + {props.children} + +); + +EmptyCard.propTypes = { + children: PropTypes.node.isRequired +}; + +export default EmptyCard; diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js index d30f24c1f..69685a22e 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -1,14 +1,17 @@ import React, {PropTypes} from 'react'; import Comment from './components/Comment'; +import EmptyCard from '../../components/EmptyCard'; import {actionsMap} from './helpers/moderationQueueActionsMap'; const ModerationQueue = ({activeTab = 'premod', ...props}) => { + const areComments = props.data[activeTab].length; return (
-
@@ -29,7 +33,12 @@ const ModerationQueue = ({activeTab = 'premod', ...props}) => { }; ModerationQueue.propTypes = { - data: PropTypes.object.isRequired + data: PropTypes.object.isRequired, + acceptComment: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, + showBanUserDialog: PropTypes.func.isRequired, + currentAsset: PropTypes.object, + suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired }; export default ModerationQueue; diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js index f8f50c6df..c8818aab9 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import timeago from 'timeago.js'; import Linkify from 'react-linkify'; import Highlighter from 'react-highlight-words'; @@ -17,7 +17,7 @@ const lang = new I18n(translations); const Comment = ({actions = [], ...props}) => { const links = linkify.getMatches(props.comment.body); - const actionSumaries = props.comment.action_summaries; + const actionSummaries = props.comment.action_summaries; return (
  • @@ -62,11 +62,31 @@ const Comment = ({actions = [], ...props}) => {

    - {actionSumaries && } + {actionSummaries && }
  • ); }; +Comment.propTypes = { + acceptComment: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, + suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired, + currentAsset: PropTypes.object, + isActive: PropTypes.bool.isRequired, + comment: PropTypes.shape({ + body: PropTypes.string.isRequired, + action_summaries: PropTypes.array, + created_at: PropTypes.string.isRequired, + user: PropTypes.shape({ + banned: PropTypes.bool + }), + asset: PropTypes.shape({ + title: PropTypes.string, + id: PropTypes.string + }) + }) +}; + const linkStyles = { backgroundColor: 'rgb(255, 219, 135)', padding: '1px 2px' diff --git a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js index bf5a34f29..6140abd90 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js @@ -5,7 +5,7 @@ const FlagBox = props => (

    Flags:

    @@ -13,7 +13,7 @@ const FlagBox = props => ( ); FlagBox.propTypes = { - actionSumaries: PropTypes.array.isRequired + actionSummaries: PropTypes.array.isRequired }; export default FlagBox; From 9000778d8fac4f027e830b7ccf589b3c880b23f6 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 21 Feb 2017 12:39:29 -0700 Subject: [PATCH 02/16] empty result for users in Community --- .../coral-admin/src/components/EmptyCard.js | 2 +- .../src/containers/Community/Community.js | 19 ++++++++++--------- .../src/containers/Community/NoResults.js | 9 --------- .../ModerationQueue/ModerationQueue.js | 6 +++++- client/coral-admin/src/translations.json | 4 +++- 5 files changed, 19 insertions(+), 21 deletions(-) delete mode 100644 client/coral-admin/src/containers/Community/NoResults.js diff --git a/client/coral-admin/src/components/EmptyCard.js b/client/coral-admin/src/components/EmptyCard.js index e0f837e69..42337a892 100644 --- a/client/coral-admin/src/components/EmptyCard.js +++ b/client/coral-admin/src/components/EmptyCard.js @@ -2,7 +2,7 @@ import React, {PropTypes} from 'react'; import {Card} from 'coral-ui'; const EmptyCard = props => ( - + {props.children} ); diff --git a/client/coral-admin/src/containers/Community/Community.js b/client/coral-admin/src/containers/Community/Community.js index bad5654cf..08a962c34 100644 --- a/client/coral-admin/src/containers/Community/Community.js +++ b/client/coral-admin/src/containers/Community/Community.js @@ -1,12 +1,12 @@ import React from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../../translations.json'; +import translations from 'coral-admin/src/translations.json'; import styles from './Community.css'; import Table from './Table'; import Loading from './Loading'; -import NoResults from './NoResults'; import {Pager} from 'coral-ui'; +import EmptyCard from '../../components/EmptyCard'; const lang = new I18n(translations); @@ -54,13 +54,14 @@ const Community = ({isFetching, commenters, ...props}) => {
    { isFetching && } - { !hasResults && } - { hasResults && - + { + hasResults + ?
    + : {lang.t('community.no-results')} } ( -
    - No users found with that user name or email address -
    -); - -export default NoResults; diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js index 69685a22e..0d48acc92 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -3,6 +3,10 @@ import React, {PropTypes} from 'react'; import Comment from './components/Comment'; import EmptyCard from '../../components/EmptyCard'; import {actionsMap} from './helpers/moderationQueueActionsMap'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-admin/src/translations'; + +const lang = new I18n(translations); const ModerationQueue = ({activeTab = 'premod', ...props}) => { const areComments = props.data[activeTab].length; @@ -25,7 +29,7 @@ const ModerationQueue = ({activeTab = 'premod', ...props}) => { currentAsset={props.currentAsset} />; }) - : No more comments to moderate! You're all caught up. Go have some ☕️ + : {lang.t('modqueue.emptyqueue')} } diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index 5b15564d7..debfdcc08 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -7,7 +7,7 @@ "admin": "Administrator", "moderator": "Moderator", "role": "Select role...", - "no-results": "No users found with that user name or email address.", + "no-results": "No users found with that user name or email address. They're hiding!", "status": "Status", "select-status": "Select status...", "active": "Active", @@ -32,6 +32,7 @@ "prevcomment": "Go to the previous comment", "singleview": "Toggle single comment edit view", "thismenu": "Open this menu", + "emptyqueue": "No more comments to moderate! You're all caught up. Go have some ☕️", "showshortcuts": "Show Shortcuts" }, "comment": { @@ -152,6 +153,7 @@ "flagged": "marcado", "shortcuts": "Atajos de teclado", "close": "Cerrar", + "emptyqueue": "No se encontro ningún usuario. Están escondidos.", "showshortcuts": "Mostrar atajos" }, "comment": { From f76befd6ac3517ade0962ab069c38eb6312378b0 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 21 Feb 2017 12:48:01 -0700 Subject: [PATCH 03/16] no comments in user list --- client/coral-settings/translations.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-settings/translations.json b/client/coral-settings/translations.json index 2a827a4f0..e255e2b64 100644 --- a/client/coral-settings/translations.json +++ b/client/coral-settings/translations.json @@ -1,12 +1,12 @@ { "en":{ - "userNoComment": "This user has not yet left a comment.", + "userNoComment": "You've never left a comment. Join the conversation!", "allComments": "All Comments", "profileSettings": "Profile Settings", "myCommentHistory": "My comment History" }, "es":{ - "userNoComment": "Aún no ha escrito ningún comentario.", + "userNoComment": "No has dejado áun ningún comentario. ¡Unete a la conversación!", "allComments": "Todos los comentarios", "profileSettings": "Configuración del perfil", "myCommentHistory": "Mi historial de comentarios" From 9149b737f5bd06f7e10994110c8a1213fcbf9cce Mon Sep 17 00:00:00 2001 From: David Jay Date: Tue, 21 Feb 2017 17:23:09 -0500 Subject: [PATCH 04/16] Retrieving counts and displaying new comments to user. --- client/coral-embed-stream/src/Comment.js | 2 +- client/coral-embed-stream/src/Embed.js | 8 +- client/coral-embed-stream/src/NewCount.js | 18 ++++ client/coral-embed-stream/src/Stream.js | 22 ++++- client/coral-framework/actions/asset.js | 1 + client/coral-framework/constants/asset.js | 1 + .../coral-framework/graphql/queries/index.js | 93 ++++++++++++------- client/coral-framework/reducers/asset.js | 3 + .../reducers/assetReducer.spec.js | 19 ++++ .../reducers/notificationReducer.spec.js | 35 +++++++ 10 files changed, 165 insertions(+), 37 deletions(-) create mode 100644 client/coral-embed-stream/src/NewCount.js create mode 100644 test/client/coral-framework/reducers/assetReducer.spec.js create mode 100644 test/client/coral-framework/reducers/notificationReducer.spec.js diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index 9d679473e..8371cec45 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -177,7 +177,7 @@ class Comment extends React.Component { comment.replies &&
    comment.replies.length} diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index a7c4dcb38..6ff153a52 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -12,6 +12,7 @@ const {fetchAssetSuccess} = assetActions; import {queryStream} from 'coral-framework/graphql/queries'; import {postComment, postFlag, postLike, deleteAction} from 'coral-framework/graphql/mutations'; import {editName} from 'coral-framework/actions/user'; +import {updateCountCache} from 'coral-framework/actions/asset'; import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework'; import Stream from './Stream'; @@ -28,6 +29,7 @@ import SettingsContainer from 'coral-settings/containers/SettingsContainer'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; import LoadMore from './LoadMore'; +import NewCount from './NewCount'; class Embed extends Component { @@ -82,7 +84,7 @@ class Embed extends Component { render () { const {activeTab} = this.state; - const {closedAt} = this.props.asset; + const {closedAt, countCache = {}} = this.props.asset; const {loading, asset, refetch} = this.props.data; const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth; @@ -147,6 +149,7 @@ class Embed extends Component { } {!loggedIn && } {loggedIn && user && } + ({ clearNotification: () => dispatch(clearNotification()), editName: (username) => dispatch(editName(username)), showSignInDialog: (offset) => dispatch(showSignInDialog(offset)), + updateCountCache: (id, count) => dispatch(updateCountCache(id, count)), logout: () => dispatch(logout()), dispatch: d => dispatch(d) }); diff --git a/client/coral-embed-stream/src/NewCount.js b/client/coral-embed-stream/src/NewCount.js new file mode 100644 index 000000000..712c36be3 --- /dev/null +++ b/client/coral-embed-stream/src/NewCount.js @@ -0,0 +1,18 @@ +import React, {PropTypes} from 'react'; + +const NewCount = ({commentCount, countCache}) => +
    + { + countCache && commentCount - countCache > 0 && +
    + Load {commentCount - countCache} More Comments +
    + } +
    ; + +NewCount.propTypes = { + commentCount: PropTypes.number.isRequired, + countCache: PropTypes.number +}; + +export default NewCount; diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index 2f0f4b01b..d59521484 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -17,10 +17,30 @@ class Stream extends React.Component { constructor(props) { super(props); - this.state = {activeReplyBox: ''}; + this.state = {activeReplyBox: '', countPoll: null}; this.setActiveReplyBox = this.setActiveReplyBox.bind(this); } + componentDidMount() { + const {asset, getCounts, updateCountCache} = this.props; + + updateCountCache(asset.id, asset.comments.length); + + // Note: Apollo's built-in polling doesn't work with fetchMore queries, so a + // setInterval is being used instead. + this.setState({ + countPoll: setInterval(() => getCounts({ + asset_id: asset.id, + limit: asset.comments.length, + sort: 'REVERSE_CHRONOLOGICAL' + }), 5000), + }); + } + + componentWillUnmount() { + clearInterval(this.state.countPoll); + } + setActiveReplyBox (reactKey) { if (!this.props.currentUser) { const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75; diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js index d5db289c3..02e72ea98 100644 --- a/client/coral-framework/actions/asset.js +++ b/client/coral-framework/actions/asset.js @@ -38,6 +38,7 @@ export const updateOpenStream = closedBody => (dispatch, getState) => { const openStream = () => ({type: actions.OPEN_COMMENTS}); const closeStream = () => ({type: actions.CLOSE_COMMENTS}); +export const updateCountCache = (id, count) => ({type: actions.UPDATE_COUNT_CACHE, id, count}); export const updateOpenStatus = status => dispatch => { if (status === 'open') { diff --git a/client/coral-framework/constants/asset.js b/client/coral-framework/constants/asset.js index 40f746706..234095d9d 100644 --- a/client/coral-framework/constants/asset.js +++ b/client/coral-framework/constants/asset.js @@ -8,3 +8,4 @@ export const UPDATE_ASSET_SETTINGS_FAILURE = 'UPDATE_ASSET_SETTINGS_FAILURE'; export const OPEN_COMMENTS = 'OPEN_COMMENTS'; export const CLOSE_COMMENTS = 'CLOSE_COMMENTS'; +export const UPDATE_COUNT_CACHE = 'UPDATE_COUNT_CACHE'; diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js index 09b3159d0..494ffa30e 100644 --- a/client/coral-framework/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -1,6 +1,7 @@ import {graphql} from 'react-apollo'; import STREAM_QUERY from './streamQuery.graphql'; import LOAD_MORE from './loadMore.graphql'; +import GET_COUNTS from './getCounts.graphql'; import MY_COMMENT_HISTORY from './myCommentHistory.graphql'; function getQueryVariable(variable) { @@ -17,6 +18,62 @@ function getQueryVariable(variable) { return 'http://localhost/default/stream'; } +export const getCounts = (data) => ({asset_id, limit, sort}) => { + return data.fetchMore({ + query: GET_COUNTS, + variables: { + asset_id, + limit, + sort + }, + updateQuery: (oldData, {fetchMoreResult:{data}}) => { + + return { + ...oldData, + asset: { + ...oldData.asset, + commentCount: data.asset.commentCount + } + }; + } + }); +}; + +export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}) => { + return data.fetchMore({ + query: LOAD_MORE, + variables: { + limit, + cursor, + parent_id, + asset_id, + sort + }, + updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => + + // If loading more replies + parent_id ? { + ...oldData, + asset: { + ...oldData.asset, + comments: oldData.asset.comments.map((comment) => + comment.id === parent_id + ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} + : comment) + } + } + + // If loading more top-level comments + : { + ...oldData, + asset: { + ...oldData.asset, + comments: [...oldData.asset.comments, ...new_top_level_comments] + } + } + }); +}; + export const queryStream = graphql(STREAM_QUERY, { options: () => ({ variables: { @@ -25,40 +82,8 @@ export const queryStream = graphql(STREAM_QUERY, { }), props: ({data}) => ({ data, - loadMore: ({limit, cursor, parent_id, asset_id, sort}) => { - return data.fetchMore({ - query: LOAD_MORE, - variables: { - limit, - cursor, - parent_id, - asset_id, - sort - }, - updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => - - // If loading more replies - parent_id ? { - ...oldData, - asset: { - ...oldData.asset, - comments: oldData.asset.comments.map((comment) => - comment.id === parent_id - ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} - : comment) - } - } - - // If loading more top-level comments - : { - ...oldData, - asset: { - ...oldData.asset, - comments: [...oldData.asset.comments, ...new_top_level_comments] - } - } - }); - } + loadMore: loadMore(data), + getCounts: getCounts(data), }) }); diff --git a/client/coral-framework/reducers/asset.js b/client/coral-framework/reducers/asset.js index f9d0a55e3..067edfc5b 100644 --- a/client/coral-framework/reducers/asset.js +++ b/client/coral-framework/reducers/asset.js @@ -19,6 +19,9 @@ export default function asset (state = initialState, action) { case actions.UPDATE_ASSET_SETTINGS_SUCCESS: return state .setIn(['settings'], action.settings); + case actions.UPDATE_COUNT_CACHE: + return state + .setIn(['countCache', action.id], action.count); default: return state; } diff --git a/test/client/coral-framework/reducers/assetReducer.spec.js b/test/client/coral-framework/reducers/assetReducer.spec.js new file mode 100644 index 000000000..c54f51737 --- /dev/null +++ b/test/client/coral-framework/reducers/assetReducer.spec.js @@ -0,0 +1,19 @@ +import {Map} from 'immutable'; +import {expect} from 'chai'; +import assetReducer from '../../../../client/coral-framework/reducers/asset'; +import * as actions from '../../../../client/coral-framework/constants/asset'; + +describe ('coral-embed-stream assetReducer', () => { + describe('UPDATE_COUNT_CACHE', () => { + it('should update the count cache', () => { + const action = { + type: actions.UPDATE_COUNT_CACHE, + id: '123', + count: 456 + }; + const store = new Map({}); + const result = assetReducer(store, action); + expect(result.getIn(['countCache', '123'])).to.equal(456); + }); + }); +}); diff --git a/test/client/coral-framework/reducers/notificationReducer.spec.js b/test/client/coral-framework/reducers/notificationReducer.spec.js new file mode 100644 index 000000000..40c50d11a --- /dev/null +++ b/test/client/coral-framework/reducers/notificationReducer.spec.js @@ -0,0 +1,35 @@ +import {Map} from 'immutable'; +import {expect} from 'chai'; +import notificationReducer from '../../../../client/coral-framework/reducers/notification'; +import * as actions from '../../../../client/coral-framework/actions/notification'; + +describe ('notificationsReducer', () => { + describe('ADD_NOTIFICATION', () => { + it('should add a notification', () => { + const action = { + type: actions.ADD_NOTIFICATION, + text: 'Test notification', + notifType: 'test' + }; + const store = new Map({}); + const result = notificationReducer(store, action); + expect(result.get('text')).to.equal(action.text); + expect(result.get('type')).to.equal(action.notifType); + }); + }); + + describe('CLEAR_NOTIFICATION', () => { + it('should clear a notification', () => { + const action = { + type: actions.CLEAR_NOTIFICATION + }; + const store = new Map({ + text: 'Test notification', + type: 'test' + }); + const result = notificationReducer(store, action); + expect(result.get('text')).to.equal(''); + expect(result.get('type')).to.equal(''); + }); + }); +}); From ca107afb9b06dad9074860f2734364241eb4ca22 Mon Sep 17 00:00:00 2001 From: David Jay Date: Tue, 21 Feb 2017 17:29:58 -0500 Subject: [PATCH 05/16] Avoiding new comment button when a comment is posted. --- client/coral-embed-stream/src/Embed.js | 2 ++ client/coral-plugin-commentbox/CommentBox.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 6ff153a52..1c7898afb 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -134,6 +134,8 @@ class Embed extends Component { postItem={this.props.postItem} appendItemArray={this.props.appendItemArray} updateItem={this.props.updateItem} + updateCountCache={this.props.updateCountCache} + countCache={countCache[asset.id]} assetId={asset.id} premod={asset.settings.moderation} isReply={false} diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index 344ade150..3d895df07 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -29,6 +29,8 @@ class CommentBox extends Component { commentPostedHandler, postItem, assetId, + updateCountCache, + countCache, parentId, addNotification, authorId @@ -44,14 +46,17 @@ class CommentBox extends Component { if (this.props.charCount && this.state.body.length > this.props.charCount) { return; } + updateCountCache(assetId, countCache + 1); postItem(comment, 'comments') .then(({data}) => { const postedComment = data.createComment.comment; if (postedComment.status === 'REJECTED') { addNotification('error', lang.t('comment-post-banned-word')); + updateCountCache(assetId, countCache); } else if (postedComment.status === 'PREMOD') { addNotification('success', lang.t('comment-post-notif-premod')); + updateCountCache(assetId, countCache); } else { addNotification('success', 'Your comment has been posted.'); } From e7c4acde4d07c47b4e053cdf95f148713ccecf15 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 21 Feb 2017 16:11:56 -0700 Subject: [PATCH 06/16] small bugfix --- .../src/containers/ModerationQueue/components/Comment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js index c8818aab9..b39eeb2f8 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js @@ -42,7 +42,7 @@ const Comment = ({actions = [], ...props}) => { /> )}
    - {props.comment.user.banned === 'banned' ? + {props.comment.user.status === 'banned' ? {lang.t('comment.banned_user')} @@ -78,7 +78,7 @@ Comment.propTypes = { action_summaries: PropTypes.array, created_at: PropTypes.string.isRequired, user: PropTypes.shape({ - banned: PropTypes.bool + status: PropTypes.string }), asset: PropTypes.shape({ title: PropTypes.string, From 80706d2fdff6ba547762d359270fe7d49671ac70 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 22 Feb 2017 10:33:47 -0700 Subject: [PATCH 07/16] fixed issue related to action create on suspect word match --- graph/mutators/comment.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 33d2c4f85..5112ba829 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -150,10 +150,8 @@ const createPublicComment = (context, commentInput) => { item_id: comment.id, item_type: 'COMMENTS', action_type: 'FLAG', - metadata: { - field: 'body', - details: 'Matched suspect word filters.' - } + group_id: 'Matched suspect word filter', + metadata: {} }) .then(() => comment); } From dd09ddf968f31c6a3848a527c89707cb215cee31 Mon Sep 17 00:00:00 2001 From: David Jay Date: Wed, 22 Feb 2017 13:13:25 -0500 Subject: [PATCH 08/16] Adding NewCount component. --- client/coral-embed-stream/src/Embed.js | 6 +++++- client/coral-embed-stream/src/NewCount.js | 6 +++--- client/coral-embed-stream/src/Stream.js | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 1c7898afb..ac9acc931 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -151,7 +151,11 @@ class Embed extends Component { } {!loggedIn && } {loggedIn && user && } - + -
    +const NewCount = ({commentCount, countCache}) =>
    { countCache && commentCount - countCache > 0 &&
    @@ -12,7 +11,8 @@ const NewCount = ({commentCount, countCache}) => NewCount.propTypes = { commentCount: PropTypes.number.isRequired, - countCache: PropTypes.number + countCache: PropTypes.number, + loadMore: PropTypes.func.isRequired }; export default NewCount; diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index d59521484..e68a223ca 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -24,7 +24,7 @@ class Stream extends React.Component { componentDidMount() { const {asset, getCounts, updateCountCache} = this.props; - updateCountCache(asset.id, asset.comments.length); + updateCountCache(asset.id, asset.commentCount); // Note: Apollo's built-in polling doesn't work with fetchMore queries, so a // setInterval is being used instead. From 2acd099f3f598252e604bffcf98dc5205d4c75d6 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 22 Feb 2017 11:15:13 -0700 Subject: [PATCH 09/16] add empty state for asset search --- .../src/containers/Streams/Streams.js | 36 ++++++++++--------- client/coral-admin/src/translations.json | 2 ++ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/client/coral-admin/src/containers/Streams/Streams.js b/client/coral-admin/src/containers/Streams/Streams.js index 62f2f009e..96130fcba 100644 --- a/client/coral-admin/src/containers/Streams/Streams.js +++ b/client/coral-admin/src/containers/Streams/Streams.js @@ -8,6 +8,7 @@ import {Link} from 'react-router'; import {Pager, Icon} from 'coral-ui'; import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl'; +import EmptyCard from 'coral-admin/src/components/EmptyCard'; const limit = 25; @@ -142,22 +143,25 @@ class Streams extends Component { {lang.t('streams.oldest')}
    -
    - - {lang.t('streams.article')} - - {lang.t('streams.pubdate')} - - - {lang.t('streams.status')} - - - -
    + { + assetsIds.length + ?
    + + {lang.t('streams.article')} + + {lang.t('streams.pubdate')} + + + {lang.t('streams.status')} + + + +
    + : {lang.t('streams.empty_result')} + }
    ); } diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index debfdcc08..7219a22ca 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -114,6 +114,7 @@ "comment_count": "Comments" }, "streams": { + "empty_result": "No assets match this search. Maybe try widening your search?", "search": "Search", "filter-streams": "Filter Streams", "stream-status": "Stream Status", @@ -222,6 +223,7 @@ "comment_count": "Comentarios" }, "streams": { + "empty_result": "No se encuentro articulo con esta busqueda. Tal vez extender la busqueda?", "search": "", "filter-streams": "", "stream-status": "", From 096bd12df6e2b73c6d3878544a2dd9039952c960 Mon Sep 17 00:00:00 2001 From: David Jay Date: Wed, 22 Feb 2017 13:45:02 -0500 Subject: [PATCH 10/16] Loading new comments. --- client/coral-embed-stream/src/Embed.js | 7 ++++ client/coral-embed-stream/src/LoadMore.js | 2 +- client/coral-embed-stream/src/NewCount.js | 35 ++++++++++++++----- .../coral-framework/graphql/queries/index.js | 5 +-- graph/typeDefs.graphql | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index ac9acc931..487437203 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -100,6 +100,10 @@ class Embed extends Component { return ; } + // 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() - 604800000); + return (
    @@ -155,6 +159,9 @@ class Embed extends Component { commentCount={asset.commentCount} countCache={countCache[asset.id]} loadMore={this.props.loadMore} + firstCommentDate={firstCommentDate} + assetId={asset.id} + updateCountCache={this.props.updateCountCache} /> { } const cursor = parentId - ? comments[1].created_at + ? comments[0].created_at : comments[comments.length - 1].created_at; loadMore({ diff --git a/client/coral-embed-stream/src/NewCount.js b/client/coral-embed-stream/src/NewCount.js index ded006325..99e1297e9 100644 --- a/client/coral-embed-stream/src/NewCount.js +++ b/client/coral-embed-stream/src/NewCount.js @@ -1,18 +1,35 @@ import React, {PropTypes} from 'react'; -const NewCount = ({commentCount, countCache}) =>
    - { - countCache && commentCount - countCache > 0 && -
    - Load {commentCount - countCache} More Comments -
    - } -
    ; +const onLoadMoreClick = ({loadMore, commentCount, firstCommentDate, assetId, updateCountCache}) => (e) => { + e.preventDefault(); + updateCountCache(assetId, commentCount); + loadMore({ + limit: 500, + cursor: firstCommentDate, + assetId, + sort: 'CHRONOLOGICAL' + }, true); +}; + +const NewCount = (props) => { + const newComments = props.commentCount - props.countCache; + + return
    + { + props.countCache && newComments > 0 && +
    + Load {newComments} More Comments +
    + } +
    ; +}; NewCount.propTypes = { commentCount: PropTypes.number.isRequired, countCache: PropTypes.number, - loadMore: PropTypes.func.isRequired + loadMore: PropTypes.func.isRequired, + assetId: PropTypes.string.isRequired, + firstCommentDate: PropTypes.string.isRequired }; export default NewCount; diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js index 494ffa30e..9b8a55134 100644 --- a/client/coral-framework/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -39,7 +39,7 @@ export const getCounts = (data) => ({asset_id, limit, sort}) => { }); }; -export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}) => { +export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}, newComments) => { return data.fetchMore({ query: LOAD_MORE, variables: { @@ -68,7 +68,8 @@ export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}) = ...oldData, asset: { ...oldData.asset, - comments: [...oldData.asset.comments, ...new_top_level_comments] + comments: newComments ? [...new_top_level_comments.reverse(), ...oldData.asset.comments] + : [...oldData.asset.comments, ...new_top_level_comments] } } }); diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 72a54a0cd..7db4dbad8 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -448,7 +448,7 @@ type RootQuery { # Comments returned based on a query. comments(query: CommentsQuery!): [Comment] - # Returne the count of comments satisfied by the query. Note that this edge is + # Return the count of comments satisfied by the query. Note that this edge is # expensive as it is not batched. Requires the `ADMIN` role. commentCount(query: CommentCountQuery!): Int From 6becb88b560306842726acd90a54f07ad9d93696 Mon Sep 17 00:00:00 2001 From: David Jay Date: Wed, 22 Feb 2017 15:07:32 -0500 Subject: [PATCH 11/16] Adding style to new comments button. --- client/coral-embed-stream/src/NewCount.js | 13 +++++++++---- client/coral-embed-stream/style/default.css | 12 ++++++++++-- client/coral-framework/translations.json | 3 +++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/client/coral-embed-stream/src/NewCount.js b/client/coral-embed-stream/src/NewCount.js index 99e1297e9..429ecc113 100644 --- a/client/coral-embed-stream/src/NewCount.js +++ b/client/coral-embed-stream/src/NewCount.js @@ -1,4 +1,7 @@ import React, {PropTypes} from 'react'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-framework/translations.json'; +const lang = new I18n(translations); const onLoadMoreClick = ({loadMore, commentCount, firstCommentDate, assetId, updateCountCache}) => (e) => { e.preventDefault(); @@ -14,12 +17,14 @@ const onLoadMoreClick = ({loadMore, commentCount, firstCommentDate, assetId, upd const NewCount = (props) => { const newComments = props.commentCount - props.countCache; - return
    + return
    { props.countCache && newComments > 0 && -
    - Load {newComments} More Comments -
    + }
    ; }; diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 1deae6026..0e4f7e4a6 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -336,18 +336,26 @@ button.coral-load-more { text-align: center; color: #FFF; background-color: #2376D8; + cursor: pointer; } button.coral-load-more:hover { background-color: #4399FF; } -.coral-load-more-replies { +.coral-load-more-replies, .coral-new-comments { width: 100%; display: flex; justify-content: center; + cursor: pointer; } -.coral-load-more-replies button.coral-load-more { +.coral-new-comments { + position: relative; + top: 1.8em; + z-index: 100; +} + +.coral-load-more-replies button.coral-load-more, .coral-new-comments button.coral-load-more{ width: initial; } diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 2884178b6..23405d2d8 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -11,6 +11,9 @@ "button": "Submit", "error": "Usernames can contain letters, numbers and _ only" }, + "newCount": "View {0} more {1}", + "comment": "comment", + "comments": "comments", "error": { "emailNotVerified": "Email address {0} not verified.", "email": "Not a valid E-Mail", From bb0b3f461a7a7809f7adf5821e59dc7e37c55317 Mon Sep 17 00:00:00 2001 From: David Jay Date: Wed, 22 Feb 2017 15:11:50 -0500 Subject: [PATCH 12/16] Moving polling interval to constant. --- client/coral-embed-stream/src/Stream.js | 3 ++- client/coral-framework/constants/comments.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index e68a223ca..89a637ceb 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -1,5 +1,6 @@ import React, {PropTypes} from 'react'; import Comment from './Comment'; +import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments'; class Stream extends React.Component { @@ -33,7 +34,7 @@ class Stream extends React.Component { asset_id: asset.id, limit: asset.comments.length, sort: 'REVERSE_CHRONOLOGICAL' - }), 5000), + }), NEW_COMMENT_COUNT_POLL_INTERVAL), }); } diff --git a/client/coral-framework/constants/comments.js b/client/coral-framework/constants/comments.js index 17ea2223e..28db9cdf9 100644 --- a/client/coral-framework/constants/comments.js +++ b/client/coral-framework/constants/comments.js @@ -1 +1,2 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10; +export const NEW_COMMENT_COUNT_POLL_INTERVAL = 20000; From 27bd1404b38181b15e7ea31528215a8841f566f8 Mon Sep 17 00:00:00 2001 From: David Jay Date: Wed, 22 Feb 2017 15:13:46 -0500 Subject: [PATCH 13/16] Adding es translation. --- client/coral-framework/translations.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 23405d2d8..c817925eb 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -42,6 +42,9 @@ "bannedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information", "editNameMsg": "", "loadMore": "Ver más", + "newCount": "Ver {0} {1} más", + "comment": "commentario", + "comments": "commentarios", "error": { "emailNotVerified": "Dirección de correo electrónico {0} no verificada.", "email": "No es un email válido", From 953c749bd2521806308387bedd06ee22dfa78708 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 22 Feb 2017 16:14:14 -0700 Subject: [PATCH 14/16] show login page instead of communicorn :'( --- client/coral-admin/src/actions/auth.js | 2 +- client/coral-admin/src/containers/LayoutContainer.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index bdb101362..c2f3a8056 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -9,7 +9,7 @@ const checkLoginFailure = error => ({type: actions.CHECK_LOGIN_FAILURE, error}); export const checkLogin = () => dispatch => { dispatch(checkLoginRequest()); - coralApi('/auth') + return coralApi('/auth') .then(result => { const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length; dispatch(checkLoginSuccess(result.user, isAdmin)); diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/LayoutContainer.js index 225e92884..5ce208503 100644 --- a/client/coral-admin/src/containers/LayoutContainer.js +++ b/client/coral-admin/src/containers/LayoutContainer.js @@ -8,7 +8,11 @@ import {PermissionRequired} from '../components/PermissionRequired'; class LayoutContainer extends Component { componentWillMount () { const {checkLogin} = this.props; - checkLogin(); + checkLogin().then(() => { + if (!this.props.auth.isAdmin) { + location.href = '/admin/login'; + } + }); } render () { const {isAdmin, loggedIn, loadingUser} = this.props.auth; From 7ae81fd17269e3954a233fcb28b3a9959f9b147b Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Thu, 23 Feb 2017 09:43:50 -0700 Subject: [PATCH 15/16] fixing mixed types --- client/coral-embed-stream/src/Embed.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 487437203..5e979d243 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -101,8 +101,9 @@ class Embed extends Component { } // 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() - 604800000); + const firstCommentDate = asset.comments[0] + ? asset.comments[0].created_at + : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); return (
    From e3b9799d4a2125377679652723eeaf8b3679fe50 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Thu, 23 Feb 2017 09:58:31 -0700 Subject: [PATCH 16/16] nested ternaries hurt me --- .../coral-framework/graphql/queries/index.js | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js index 9b8a55134..c993dfb6a 100644 --- a/client/coral-framework/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -49,29 +49,38 @@ export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}, n asset_id, sort }, - updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => + updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => { - // If loading more replies - parent_id ? { - ...oldData, - asset: { - ...oldData.asset, - comments: oldData.asset.comments.map((comment) => - comment.id === parent_id - ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} - : comment) - } + let updatedAsset; + + if (parent_id) { + + // If loading more replies + updatedAsset = { + ...oldData, + asset: { + ...oldData.asset, + comments: oldData.asset.comments.map((comment) => + comment.id === parent_id + ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} + : 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] + } + }; } - // If loading more top-level comments - : { - ...oldData, - asset: { - ...oldData.asset, - comments: newComments ? [...new_top_level_comments.reverse(), ...oldData.asset.comments] - : [...oldData.asset.comments, ...new_top_level_comments] - } - } + return updatedAsset; + } }); };