From 71d4ddf03053b814668f4bc1d8cf5aa418231c99 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 23 Nov 2016 13:28:20 -0700 Subject: [PATCH 01/18] create comment history class --- .../CommentHistory.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 client/coral-plugin-comment-history/CommentHistory.js diff --git a/client/coral-plugin-comment-history/CommentHistory.js b/client/coral-plugin-comment-history/CommentHistory.js new file mode 100644 index 000000000..492c1545b --- /dev/null +++ b/client/coral-plugin-comment-history/CommentHistory.js @@ -0,0 +1,20 @@ +import React from 'react'; +import {connect} from 'react-redux'; + +const mapStateToProps = state => { + return { + config: state.config.toJS(), + items: state.items.toJS(), + auth: state.auth.toJS() + }; +}; + +class CommentHistory extends React.Component { + render () { + return ( +
Comment History
+ ); + } +} + +export default connect(mapStateToProps)(CommentHistory); From 28520bfd4aec38692dac9422d5c1b328730f21d6 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 23 Nov 2016 17:14:51 -0700 Subject: [PATCH 02/18] add get comments by user endpoint. refactor getStream to be functional programming --- .../coral-embed-stream/src/CommentStream.js | 2 + client/coral-framework/actions/items.js | 59 +++++++++++++------ client/coral-framework/reducers/items.js | 4 +- .../CommentCount.js | 17 +++--- .../CommentHistory.js | 0 models/comment.js | 9 +++ routes/api/comments/index.js | 2 + 7 files changed, 67 insertions(+), 26 deletions(-) rename client/{coral-plugin-comment-history => coral-plugin-history}/CommentHistory.js (100%) diff --git a/client/coral-embed-stream/src/CommentStream.js b/client/coral-embed-stream/src/CommentStream.js index a0eaca044..6da2bd22b 100644 --- a/client/coral-embed-stream/src/CommentStream.js +++ b/client/coral-embed-stream/src/CommentStream.js @@ -19,6 +19,7 @@ import LikeButton from '../../coral-plugin-likes/LikeButton'; import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton'; import SignInContainer from '../../coral-sign-in/containers/SignInContainer'; import UserBox from '../../coral-sign-in/components/UserBox'; +import CommentHistory from '../../coral-plugin-history/CommentHistory'; const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions; const {addNotification, clearNotification} = notificationActions; @@ -87,6 +88,7 @@ class CommentStream extends Component { const {actions, users, comments} = this.props.items; const {loggedIn, user, showSignInDialog} = this.props.auth; return
+ { rootItem ?
diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index f922280c1..148dabbfa 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -1,5 +1,11 @@ +import sortBy from 'lodash/sortBy'; + /* Item Actions */ +export const REQUEST_COMMENTS_BY_USER = 'REQUEST_COMMENTS_BY_USER'; +export const RECEIVE_COMMENTS_BY_USER = 'RECEIVE_COMMENTS_BY_USER'; +export const FAILURE_COMMENTS_BY_USER = 'FAILURE_COMMENTS_BY_USER'; + /** * Action name constants */ @@ -84,6 +90,27 @@ export const appendItemArray = (id, property, value, add_to_front, item_type) => }; }; +/** + * + * Get a list of comments by a single user + * + * @param {string} user_id + * @returns Promise + */ +export const fetchCommentsByUserId = userId => { + return (dispatch) => { + dispatch({type: REQUEST_COMMENTS_BY_USER}); + return fetch(`/api/v1/comments?user_id=${userId}`, getInit('GET')) + .then(responseHandler) + .then(comments => { + dispatch({type: RECEIVE_COMMENTS_BY_USER, comments}); + }) + .catch(error => { + dispatch({type: FAILURE_COMMENTS_BY_USER, error}); + }); + }; +}; + /* * Get Items from Query * Gets a set of items from a predefined query @@ -104,25 +131,24 @@ export function getStream (assetUrl) { .then((json) => { /* Add items to the store */ - const itemTypes = Object.keys(json); - for (let i = 0; i < itemTypes.length; i++ ) { - if (itemTypes[i] === 'actions') { - for (let j = 0; j < json[itemTypes[i]].length; j++ ) { - let action = json[itemTypes[i]][j]; + Object.keys(json).forEach(type => { + if (type === 'actions') { + json[type].forEach(action => { action.id = `${action.action_type}_${action.item_id}`; dispatch(addItem(action, 'actions')); - } + }); } else { - for (let j = 0; j < json[itemTypes[i]].length; j++ ) { - dispatch(addItem(json[itemTypes[i]][j], itemTypes[i])); - } + json[type].forEach(item => { + dispatch(addItem(item, type)); + }); } - } + }); const assetId = json.assets[0].id; /* Sort comments by date*/ json.comments.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); + const rels = json.comments.reduce((h, item) => { /* Check for root and child comments. */ if ( @@ -140,15 +166,14 @@ export function getStream (assetUrl) { dispatch(updateItem(assetId, 'comments', rels.rootComments, 'assets')); - const childKeys = Object.keys(rels.childComments); - for (let i = 0; i < childKeys.length; i++ ) { - dispatch(updateItem(childKeys[i], 'children', rels.childComments[childKeys[i]].reverse(), 'comments')); - } + Object.keys(rels.childComments).forEach(key => { + dispatch(updateItem(key, 'children', rels.childComments[key].reverse(), 'comments')); + }); /* Hydrate actions on comments */ - for (let i = 0; i < json.actions.length; i++ ) { - dispatch(updateItem(json.actions[i].item_id, json.actions[i].action_type, json.actions[i].id, 'comments')); - } + json.actions.forEach(action => { + dispatch(updateItem(action.item_id, action.action_type, action.id, 'comments')); + }); return (json); }); diff --git a/client/coral-framework/reducers/items.js b/client/coral-framework/reducers/items.js index 17569c545..fa14085f3 100644 --- a/client/coral-framework/reducers/items.js +++ b/client/coral-framework/reducers/items.js @@ -6,7 +6,7 @@ import * as actions from '../actions/items'; const initialState = fromJS({ comments: {}, users: {}, - actions: {} + actions: {} }); export default (state = initialState, action) => { @@ -17,7 +17,7 @@ export default (state = initialState, action) => { return state.setIn([action.item_type, action.id, action.property], fromJS(action.value)); case actions.APPEND_ITEM_ARRAY: return state.updateIn([action.item_type, action.id, action.property], (prop) => { - console.log(prop); + console.log(action, prop); if (action.add_to_front) { return prop ? prop.unshift(fromJS(action.value)) : fromJS([action.value]); } else { diff --git a/client/coral-plugin-comment-count/CommentCount.js b/client/coral-plugin-comment-count/CommentCount.js index 7a27c1982..87f656c22 100644 --- a/client/coral-plugin-comment-count/CommentCount.js +++ b/client/coral-plugin-comment-count/CommentCount.js @@ -1,20 +1,23 @@ import React from 'react'; import {I18n} from '../coral-framework'; import translations from './translations.json'; +import has from 'lodash/has'; +import reduce from 'lodash/reduce'; const name = 'coral-plugin-comment-count'; const CommentCount = ({items, id}) => { let count = 0; - if (items.assets[id] && items.assets[id].comments) { + if (has(items, `assets.${id}.comments`)) { count += items.assets[id].comments.length; } - const itemKeys = Object.keys(items.comments); - for (let i = 0; i < itemKeys.length; i++) { - const item = items.comments[itemKeys[i]]; - if (item.children) { - count += item.children.length; + + // lodash reduce works on {} + count += reduce(items.comments, (accum, comment) => { + if (comment.children) { + accum += comment.children.length; } - } + return accum; + }, 0); return
{`${count} ${count === 1 ? lang.t('comment') : lang.t('comment-plural')}`} diff --git a/client/coral-plugin-comment-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js similarity index 100% rename from client/coral-plugin-comment-history/CommentHistory.js rename to client/coral-plugin-history/CommentHistory.js diff --git a/models/comment.js b/models/comment.js index 2ac130978..2aedefe62 100644 --- a/models/comment.js +++ b/models/comment.js @@ -210,6 +210,15 @@ CommentSchema.statics.all = () => { return Comment.find(); }; +/** + * Returns all the comments by user + * probably to be paginated at some point in the future + * @return {Promise} array resolves to an array of comments by that user + */ +CommentSchema.statics.findByUserId = function (author_id) { + return Comment.find({author_id}); +}; + // Comment model. const Comment = mongoose.model('Comment', CommentSchema); diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index d318857d6..3fdbca334 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -11,6 +11,8 @@ router.get('/', (req, res, next) => { query = Comment.findByStatus(req.query.status); } else if (req.query.action_type) { query = Comment.findByActionType(req.query.action_type); + } else if (req.query.user_id) { + query = Comment.findByUserId(req.query.user_id); } else { query = Comment.all(); } From d10870c6bbb73906357ca9efaf2ce2ce1743c497 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Mon, 28 Nov 2016 10:54:10 -0700 Subject: [PATCH 03/18] dispatch update for adding comemnts --- client/coral-framework/actions/items.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index 148dabbfa..b4e832bd3 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -104,6 +104,11 @@ export const fetchCommentsByUserId = userId => { .then(responseHandler) .then(comments => { dispatch({type: RECEIVE_COMMENTS_BY_USER, comments}); + + comments.forEach(comment => { + dispatch(addItem(comment, 'comments')); + }); + }) .catch(error => { dispatch({type: FAILURE_COMMENTS_BY_USER, error}); From c9a8d448bab80a79d70714527d042fc4fe569d7e Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Mon, 12 Dec 2016 12:20:38 -0700 Subject: [PATCH 04/18] use plugin CommentHistory component --- client/coral-framework/actions/items.js | 2 -- .../CommentHistory.css | 0 client/coral-plugin-history/CommentHistory.js | 20 +++++++++++-------- .../containers/SettingsContainer.js | 6 ++++-- 4 files changed, 16 insertions(+), 12 deletions(-) rename client/{coral-settings/components => coral-plugin-history}/CommentHistory.css (100%) diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index 6cdc1adeb..01327a324 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -13,8 +13,6 @@ export const ADD_ITEM = 'ADD_ITEM'; export const UPDATE_ITEM = 'UPDATE_ITEM'; export const APPEND_ITEM_ARRAY = 'APPEND_ITEM_ARRAY'; -/* Item Actions */ - /** * Action creators */ diff --git a/client/coral-settings/components/CommentHistory.css b/client/coral-plugin-history/CommentHistory.css similarity index 100% rename from client/coral-settings/components/CommentHistory.css rename to client/coral-plugin-history/CommentHistory.css diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index 492c1545b..ea8846373 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -1,6 +1,18 @@ import React from 'react'; import {connect} from 'react-redux'; +import styles from './CommentHistory.css'; + +class CommentHistory extends React.Component { + render () { + return ( +
+

Comment History

+
+ ); + } +} + const mapStateToProps = state => { return { config: state.config.toJS(), @@ -9,12 +21,4 @@ const mapStateToProps = state => { }; }; -class CommentHistory extends React.Component { - render () { - return ( -
Comment History
- ); - } -} - export default connect(mapStateToProps)(CommentHistory); diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 96020a10c..916a85baf 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -6,10 +6,12 @@ import {saveBio} from 'coral-framework/actions/user'; import BioContainer from './BioContainer'; import NotLoggedIn from '../components/NotLoggedIn'; import {TabBar, Tab, TabContent} from '../../coral-ui'; -import CommentHistory from '../components/CommentHistory'; +import CommentHistory from 'coral-plugin-history/CommentHistory'; import SettingsHeader from '../components/SettingsHeader'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; +import {fetchCommentsByUserId} from 'coral-framework/actions/items'; + class SignInContainer extends Component { constructor (props) { super(props); @@ -56,7 +58,7 @@ const mapStateToProps = () => ({ const mapDispatchToProps = dispatch => ({ saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)), - getHistory: () => dispatch(), + fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)) }); export default connect( From c999e1c1a9df8969c726014d614efba1dcc2d783 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Mon, 12 Dec 2016 12:28:10 -0700 Subject: [PATCH 05/18] use new coralApi fetch --- client/coral-framework/actions/items.js | 3 +-- client/coral-settings/containers/SettingsContainer.js | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index 01327a324..e2dfc1ee9 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -92,8 +92,7 @@ export const appendItemArray = (id, property, value, add_to_front, item_type) => export const fetchCommentsByUserId = userId => { return (dispatch) => { dispatch({type: REQUEST_COMMENTS_BY_USER}); - return fetch(`/api/v1/comments?user_id=${userId}`, getInit('GET')) - .then(responseHandler) + return coralApi(`/comments?user_id=${userId}`) .then(comments => { dispatch({type: RECEIVE_COMMENTS_BY_USER, comments}); diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 916a85baf..913f5f62e 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -24,6 +24,8 @@ class SignInContainer extends Component { componentWillMount () { // Fetch commentHistory + console.log('userData', this.props.userData); + this.props.fetchCommentsByUserId(this.props.userData.id); } handleTabChange(tab) { From c016e29290835940fbc41e430363975ac3ccbb7c Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Mon, 12 Dec 2016 13:00:39 -0700 Subject: [PATCH 06/18] separate route for loading comments per user --- client/coral-framework/actions/items.js | 6 ++++-- routes/api/comments/index.js | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index e2dfc1ee9..f9f0157c7 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -92,16 +92,19 @@ export const appendItemArray = (id, property, value, add_to_front, item_type) => export const fetchCommentsByUserId = userId => { return (dispatch) => { dispatch({type: REQUEST_COMMENTS_BY_USER}); - return coralApi(`/comments?user_id=${userId}`) + return coralApi(`/comments/user/${userId}`) .then(comments => { dispatch({type: RECEIVE_COMMENTS_BY_USER, comments}); + console.log('comments?', comments); + comments.forEach(comment => { dispatch(addItem(comment, 'comments')); }); }) .catch(error => { + console.error('FAILURE_COMMENTS_BY_USER', error); dispatch({type: FAILURE_COMMENTS_BY_USER, error}); }); }; @@ -145,7 +148,6 @@ export function getStream (assetUrl) { /* Sort comments by date*/ json.comments.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - const rels = json.comments.reduce((h, item) => { /* Check for root and child comments. */ if ( diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index 168b89fc3..ec01c2984 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -116,6 +116,18 @@ router.post('/', wordlist.filter('body'), (req, res, next) => { }); }); +router.get('/user/:user_id', (req, res, next) => { + // how to only get YOUR comments? + Comment.findByUserId(req.params.user_id) + .then(comments => { + res.json(comments); + }) + .catch(error => { + error.status = 500; + next(error); + }); +}); + router.get('/:comment_id', authorization.needed('admin'), (req, res, next) => { Comment .findById(req.params.comment_id) From d43cd9caf9df3010ea262dff6363bca6b2d895e3 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Mon, 12 Dec 2016 16:26:02 -0700 Subject: [PATCH 07/18] list comments out --- client/coral-framework/actions/items.js | 31 ---------------- client/coral-framework/actions/user.js | 27 ++++++++++++++ client/coral-framework/constants/user.js | 3 ++ client/coral-framework/reducers/items.js | 1 - client/coral-framework/reducers/user.js | 7 ++-- client/coral-plugin-history/CommentHistory.js | 35 +++++++++---------- .../components/CommentHistory.js | 15 -------- .../containers/SettingsContainer.js | 12 +++---- 8 files changed, 57 insertions(+), 74 deletions(-) delete mode 100644 client/coral-settings/components/CommentHistory.js diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js index f9f0157c7..cd798c6b9 100644 --- a/client/coral-framework/actions/items.js +++ b/client/coral-framework/actions/items.js @@ -6,9 +6,6 @@ import {UPDATE_CONFIG} from '../constants/config'; * Action name constants */ -export const REQUEST_COMMENTS_BY_USER = 'REQUEST_COMMENTS_BY_USER'; -export const RECEIVE_COMMENTS_BY_USER = 'RECEIVE_COMMENTS_BY_USER'; -export const FAILURE_COMMENTS_BY_USER = 'FAILURE_COMMENTS_BY_USER'; export const ADD_ITEM = 'ADD_ITEM'; export const UPDATE_ITEM = 'UPDATE_ITEM'; export const APPEND_ITEM_ARRAY = 'APPEND_ITEM_ARRAY'; @@ -82,34 +79,6 @@ export const appendItemArray = (id, property, value, add_to_front, item_type) => }; }; -/** - * - * Get a list of comments by a single user - * - * @param {string} user_id - * @returns Promise - */ -export const fetchCommentsByUserId = userId => { - return (dispatch) => { - dispatch({type: REQUEST_COMMENTS_BY_USER}); - return coralApi(`/comments/user/${userId}`) - .then(comments => { - dispatch({type: RECEIVE_COMMENTS_BY_USER, comments}); - - console.log('comments?', comments); - - comments.forEach(comment => { - dispatch(addItem(comment, 'comments')); - }); - - }) - .catch(error => { - console.error('FAILURE_COMMENTS_BY_USER', error); - dispatch({type: FAILURE_COMMENTS_BY_USER, error}); - }); - }; -}; - /* * Get Items from Query * Gets a set of items from a predefined query diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index cda2d765d..5cbd04792 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,5 +1,6 @@ import * as actions from '../constants/user'; import {addNotification} from '../actions/notification'; +import {addItem} from '../actions/items'; import coralApi from '../helpers/response'; import I18n from 'coral-framework/modules/i18n/i18n'; @@ -19,3 +20,29 @@ export const saveBio = (user_id, formData) => dispatch => { }) .catch(error => dispatch(saveBioFailure(error))); }; + +/** + * + * Get a list of comments by a single user + * + * @param {string} user_id + * @returns Promise + */ +export const fetchCommentsByUserId = userId => { + return (dispatch) => { + dispatch({type: actions.REQUEST_COMMENTS_BY_USER}); + return coralApi(`/comments/user/${userId}`) + .then(comments => { + comments.forEach(comment => { + dispatch(addItem(comment, 'comments')); + }); + + dispatch({type: actions.RECEIVE_COMMENTS_BY_USER, comments: comments.map(comment => comment.id)}); + }) + .catch(error => { + console.error(error.stack); + console.error('FAILURE_COMMENTS_BY_USER', error); + dispatch({type: actions.FAILURE_COMMENTS_BY_USER, error}); + }); + }; +}; diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js index 0c316d48a..ce9af61a2 100644 --- a/client/coral-framework/constants/user.js +++ b/client/coral-framework/constants/user.js @@ -1,3 +1,6 @@ export const SAVE_BIO_REQUEST = 'SAVE_BIO_REQUEST'; export const SAVE_BIO_SUCCESS = 'SAVE_BIO_SUCCESS'; export const SAVE_BIO_FAILURE = 'SAVE_BIO_FAILURE'; +export const REQUEST_COMMENTS_BY_USER = 'REQUEST_COMMENTS_BY_USER'; +export const RECEIVE_COMMENTS_BY_USER = 'RECEIVE_COMMENTS_BY_USER'; +export const FAILURE_COMMENTS_BY_USER = 'FAILURE_COMMENTS_BY_USER'; diff --git a/client/coral-framework/reducers/items.js b/client/coral-framework/reducers/items.js index fa14085f3..93388c1ab 100644 --- a/client/coral-framework/reducers/items.js +++ b/client/coral-framework/reducers/items.js @@ -17,7 +17,6 @@ export default (state = initialState, action) => { return state.setIn([action.item_type, action.id, action.property], fromJS(action.value)); case actions.APPEND_ITEM_ARRAY: return state.updateIn([action.item_type, action.id, action.property], (prop) => { - console.log(action, prop); if (action.add_to_front) { return prop ? prop.unshift(fromJS(action.value)) : fromJS([action.value]); } else { diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index 11b57fc15..c7badf458 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -1,11 +1,12 @@ -import {Map} from 'immutable'; +import {Map, fromJS} from 'immutable'; import * as authActions from '../constants/auth'; import * as actions from '../constants/user'; const initialState = Map({ displayName: '', profiles: [], - settings: {} + settings: {}, + myComments: [] }); const purge = user => { @@ -30,6 +31,8 @@ export default function user (state = initialState, action) { case actions.SAVE_BIO_SUCCESS: return state .set('settings', action.settings); + case actions.RECEIVE_COMMENTS_BY_USER: + return state.set('myComments', fromJS(action.comments)); default : return state; } diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index ea8846373..de95ff2b2 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -1,24 +1,21 @@ -import React from 'react'; -import {connect} from 'react-redux'; +import React, {PropTypes} from 'react'; import styles from './CommentHistory.css'; -class CommentHistory extends React.Component { - render () { - return ( -
-

Comment History

-
- ); - } -} - -const mapStateToProps = state => { - return { - config: state.config.toJS(), - items: state.items.toJS(), - auth: state.auth.toJS() - }; +const CommentHistory = props => { + return ( +
+

Comment History

+ {props.comments.map((comment, i) => { + console.log('a comment', comment); + return

{comment.body}

; + })} +
+ ); }; -export default connect(mapStateToProps)(CommentHistory); +CommentHistory.propTypes = { + comments: PropTypes.arrayOf(PropTypes.object).isRequired +}; + +export default CommentHistory; diff --git a/client/coral-settings/components/CommentHistory.js b/client/coral-settings/components/CommentHistory.js deleted file mode 100644 index 3160c8a88..000000000 --- a/client/coral-settings/components/CommentHistory.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import styles from './CommentHistory.css'; - -export default ({comments = []}) => ( -
-

Comments

-
    - {comments.map(() => ( -
  • - {/* Comment Data*/} -
  • - ))} -
-
-); diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 913f5f62e..9ad489816 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; -import {saveBio} from 'coral-framework/actions/user'; +import {saveBio, fetchCommentsByUserId} from 'coral-framework/actions/user'; import BioContainer from './BioContainer'; import NotLoggedIn from '../components/NotLoggedIn'; @@ -10,8 +10,6 @@ import CommentHistory from 'coral-plugin-history/CommentHistory'; import SettingsHeader from '../components/SettingsHeader'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; -import {fetchCommentsByUserId} from 'coral-framework/actions/items'; - class SignInContainer extends Component { constructor (props) { super(props); @@ -35,7 +33,7 @@ class SignInContainer extends Component { } render() { - const {loggedIn, userData, showSignInDialog} = this.props; + const {loggedIn, userData, showSignInDialog, items, user} = this.props; const {activeTab} = this.state; return ( }> @@ -45,7 +43,7 @@ class SignInContainer extends Component { Profile Settings - + items.comments[id])} /> @@ -55,7 +53,9 @@ class SignInContainer extends Component { } } -const mapStateToProps = () => ({ +const mapStateToProps = state => ({ + items: state.items.toJS(), + user: state.user.toJS() }); const mapDispatchToProps = dispatch => ({ From 37d8c3ad38a1490fb2663cb9e86a407e3edf19a6 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 13 Dec 2016 12:40:06 -0700 Subject: [PATCH 08/18] try to request a list of assets and fail --- client/coral-framework/actions/assets.js | 19 +++++++++++++++++++ client/coral-plugin-history/Comment.css | 7 +++++++ client/coral-plugin-history/Comment.js | 18 ++++++++++++++++++ client/coral-plugin-history/CommentHistory.js | 6 +++--- .../containers/SettingsContainer.js | 13 ++++++++++--- models/asset.js | 10 ++++++++++ routes/api/asset/index.js | 14 ++++++++++++++ 7 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 client/coral-framework/actions/assets.js create mode 100644 client/coral-plugin-history/Comment.css create mode 100644 client/coral-plugin-history/Comment.js diff --git a/client/coral-framework/actions/assets.js b/client/coral-framework/actions/assets.js new file mode 100644 index 000000000..226f1b2b9 --- /dev/null +++ b/client/coral-framework/actions/assets.js @@ -0,0 +1,19 @@ +import coralApi from '../helpers/response'; +import addItem from './items'; + +export const FETCH_MULTIPLE_ASSETS = 'FETCH_MULTIPLE_ASSETS'; +export const RECEIVE_MULTIPLE_ASSETS = 'RECEIVE_MULTIPLE_ASSETS'; +export const FAILURE_MULTIPLE_ASSSETS = 'FAILURE_MULTIPLE_ASSSETS'; + +export const fetchMulitpleAssets = ids => { + return dispatch => { + dispatch({type: FETCH_MULTIPLE_ASSETS}); + + coralApi(`/asset/multi?ids=${encodeURIComponent(ids.join(','))}`) + .then(assets => { + dispatch({type: RECEIVE_MULTIPLE_ASSETS, assets}); + console.log('assets!', assets); + }) + .catch(error => dispatch({type: FAILURE_MULTIPLE_ASSSETS, error})); + }; +}; diff --git a/client/coral-plugin-history/Comment.css b/client/coral-plugin-history/Comment.css new file mode 100644 index 000000000..121839a1d --- /dev/null +++ b/client/coral-plugin-history/Comment.css @@ -0,0 +1,7 @@ +.assetURL { + +} + +.commentBody { + +} diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js new file mode 100644 index 000000000..a14320ecd --- /dev/null +++ b/client/coral-plugin-history/Comment.js @@ -0,0 +1,18 @@ +import React, {PropTypes} from 'react'; + +import styles from './Comment.css'; + +const Comment = props => { + return ( +
+

{props.comment.asset_id}

+

{props.comment.body}

+
+ ); +}; + +Comment.propTypes = { + comment: PropTypes.object.isRequired +}; + +export default Comment; diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index de95ff2b2..cdeaaf3cd 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -1,14 +1,14 @@ import React, {PropTypes} from 'react'; - +import Comment from './Comment'; import styles from './CommentHistory.css'; const CommentHistory = props => { return (
-

Comment History

+

All Comments

{props.comments.map((comment, i) => { console.log('a comment', comment); - return

{comment.body}

; + return ; })}
); diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 9ad489816..0c63cc77d 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -2,6 +2,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {saveBio, fetchCommentsByUserId} from 'coral-framework/actions/user'; +import {fetchMulitpleAssets} from 'coral-framework/actions/assets'; import BioContainer from './BioContainer'; import NotLoggedIn from '../components/NotLoggedIn'; @@ -22,8 +23,13 @@ class SignInContainer extends Component { componentWillMount () { // Fetch commentHistory - console.log('userData', this.props.userData); - this.props.fetchCommentsByUserId(this.props.userData.id); + this.props.fetchCommentsByUserId(this.props.userData.id) + .then(() => { + const assetIds = this.props.user.myComments + .map(id => this.props.items.comments[id]) + .map(comment => comment.asset_id); + this.props.fetchMulitpleAssets(assetIds); + }); } handleTabChange(tab) { @@ -60,7 +66,8 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)), - fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)) + fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)), + fetchMulitpleAssets: assetIds => dispatch(fetchMulitpleAssets(assetIds)) }); export default connect( diff --git a/models/asset.js b/models/asset.js index 3c68b7c13..6441e00f3 100644 --- a/models/asset.js +++ b/models/asset.js @@ -152,6 +152,16 @@ AssetSchema.statics.search = (value) => value.length === 0 ? Asset.find({}) : As } }); +/** + * Finds multiple assets with matching ids + * @param {Array} ids an array of Strings of asset_id + * @return {Promise} resolves to list of Assets + */ +AssetSchema.statics.findMultipleById = function (ids) { + const query = ids.map(id => ({id})); + return Asset.find(query); +}; + const Asset = mongoose.model('Asset', AssetSchema); module.exports = Asset; diff --git a/routes/api/asset/index.js b/routes/api/asset/index.js index 2ddc3ea23..1090c8e58 100644 --- a/routes/api/asset/index.js +++ b/routes/api/asset/index.js @@ -58,6 +58,20 @@ router.get('/:asset_id', (req, res, next) => { }); }); +// get multiple assets with a comma-separated list of asset ids +router.get('/multi', (req, res, next) => { + const assetIds = req.query.ids.split(','); + + Asset.findMultipleById(assetIds) + .then(assets => { + res.json(assets); + }) + .catch(error => { + error.status = 500; + next(error); + }); +}); + // Adds the asset id to the queue to be scraped. router.post('/:asset_id/scrape', (req, res, next) => { From 5962037c21766bf035ecad14ea11d687272e3d8f Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Tue, 13 Dec 2016 16:03:50 -0700 Subject: [PATCH 09/18] display list of comments with asset url --- client/coral-framework/actions/assets.js | 8 +++--- client/coral-framework/reducers/items.js | 1 + client/coral-framework/reducers/user.js | 6 +++- client/coral-plugin-history/Comment.js | 9 ++++-- client/coral-plugin-history/CommentHistory.js | 10 +++++-- .../containers/SettingsContainer.js | 8 +++++- routes/api/assets/index.js | 28 +++++++++---------- 7 files changed, 45 insertions(+), 25 deletions(-) diff --git a/client/coral-framework/actions/assets.js b/client/coral-framework/actions/assets.js index 226f1b2b9..5de9a86d5 100644 --- a/client/coral-framework/actions/assets.js +++ b/client/coral-framework/actions/assets.js @@ -1,5 +1,5 @@ import coralApi from '../helpers/response'; -import addItem from './items'; +import {addItem} from './items'; export const FETCH_MULTIPLE_ASSETS = 'FETCH_MULTIPLE_ASSETS'; export const RECEIVE_MULTIPLE_ASSETS = 'RECEIVE_MULTIPLE_ASSETS'; @@ -9,10 +9,10 @@ export const fetchMulitpleAssets = ids => { return dispatch => { dispatch({type: FETCH_MULTIPLE_ASSETS}); - coralApi(`/asset/multi?ids=${encodeURIComponent(ids.join(','))}`) + coralApi(`/assets/multi?ids=${encodeURIComponent(ids.join(','))}`) .then(assets => { - dispatch({type: RECEIVE_MULTIPLE_ASSETS, assets}); - console.log('assets!', assets); + assets.forEach(asset => dispatch(addItem(asset, 'assets'))); + dispatch({type: RECEIVE_MULTIPLE_ASSETS, assets: assets.map(asset => asset.id)}); }) .catch(error => dispatch({type: FAILURE_MULTIPLE_ASSSETS, error})); }; diff --git a/client/coral-framework/reducers/items.js b/client/coral-framework/reducers/items.js index 93388c1ab..268557809 100644 --- a/client/coral-framework/reducers/items.js +++ b/client/coral-framework/reducers/items.js @@ -6,6 +6,7 @@ import * as actions from '../actions/items'; const initialState = fromJS({ comments: {}, users: {}, + assets: {}, actions: {} }); diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index c7badf458..c4d061b42 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -1,12 +1,14 @@ import {Map, fromJS} from 'immutable'; import * as authActions from '../constants/auth'; import * as actions from '../constants/user'; +import * as assetActions from '../actions/assets'; const initialState = Map({ displayName: '', profiles: [], settings: {}, - myComments: [] + myComments: [], + myAssets: [] // the assets from which myComments (above) originated }); const purge = user => { @@ -33,6 +35,8 @@ export default function user (state = initialState, action) { .set('settings', action.settings); case actions.RECEIVE_COMMENTS_BY_USER: return state.set('myComments', fromJS(action.comments)); + case assetActions.RECEIVE_MULTIPLE_ASSETS: + return state.set('myAssets', fromJS(action.assets)); default : return state; } diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js index a14320ecd..c7a1f5336 100644 --- a/client/coral-plugin-history/Comment.js +++ b/client/coral-plugin-history/Comment.js @@ -5,14 +5,19 @@ import styles from './Comment.css'; const Comment = props => { return (
-

{props.comment.asset_id}

+

{props.asset.url}

{props.comment.body}

); }; Comment.propTypes = { - comment: PropTypes.object.isRequired + comment: PropTypes.shape({ + body: PropTypes.string + }).isRequired, + asset: PropTypes.shape({ + url: PropTypes.string + }).isRequired }; export default Comment; diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index cdeaaf3cd..b937e87e9 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -7,15 +7,19 @@ const CommentHistory = props => {

All Comments

{props.comments.map((comment, i) => { - console.log('a comment', comment); - return ; + const asset = props.assets.find(asset => asset.id === comment.asset_id); + return ; })}
); }; CommentHistory.propTypes = { - comments: PropTypes.arrayOf(PropTypes.object).isRequired + comments: PropTypes.arrayOf(PropTypes.object).isRequired, + assets: PropTypes.arrayOf(PropTypes.object).isRequired }; export default CommentHistory; diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 0c63cc77d..9d535d595 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -49,7 +49,13 @@ class SignInContainer extends Component { Profile Settings - items.comments[id])} /> + { + user.myComments.length && user.myAssets.length + ? items.comments[id])} + assets={user.myAssets.map(id => items.assets[id])} /> + :

Loading comment history...

+ }
diff --git a/routes/api/assets/index.js b/routes/api/assets/index.js index 5229afe97..20fbf1630 100644 --- a/routes/api/assets/index.js +++ b/routes/api/assets/index.js @@ -40,6 +40,20 @@ router.get('/', (req, res, next) => { }); +// get multiple assets with a comma-separated list of asset ids +router.get('/multi', (req, res, next) => { + const assetIds = req.query.ids.split(','); + + Asset.findMultipleById(assetIds) + .then(assets => { + res.json(assets); + }) + .catch(error => { + error.status = 500; + next(error); + }); +}); + // Get an asset by id. router.get('/:asset_id', (req, res, next) => { @@ -58,20 +72,6 @@ router.get('/:asset_id', (req, res, next) => { }); }); -// get multiple assets with a comma-separated list of asset ids -router.get('/multi', (req, res, next) => { - const assetIds = req.query.ids.split(','); - - Asset.findMultipleById(assetIds) - .then(assets => { - res.json(assets); - }) - .catch(error => { - error.status = 500; - next(error); - }); -}); - // Adds the asset id to the queue to be scraped. router.post('/:asset_id/scrape', (req, res, next) => { From 624eaeee11ae39a48933a75fa1fcacd31c9ba84c Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 12:01:32 -0700 Subject: [PATCH 10/18] move tests to tests directory --- client/coral-plugin-history/Comment.css | 3 +- client/coral-plugin-history/Comment.js | 6 ++-- package.json | 2 ++ .../coral-plugin-history/Comment.spec.js | 31 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/client/coral-plugin-history/Comment.spec.js diff --git a/client/coral-plugin-history/Comment.css b/client/coral-plugin-history/Comment.css index 121839a1d..ba2a59f4b 100644 --- a/client/coral-plugin-history/Comment.css +++ b/client/coral-plugin-history/Comment.css @@ -1,5 +1,6 @@ .assetURL { - + font-size: 16px; + color: black; } .commentBody { diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js index c7a1f5336..c54a0feef 100644 --- a/client/coral-plugin-history/Comment.js +++ b/client/coral-plugin-history/Comment.js @@ -5,8 +5,10 @@ import styles from './Comment.css'; const Comment = props => { return (
-

{props.asset.url}

-

{props.comment.body}

+

+ {props.asset.url} +

+

{props.comment.body}

); }; diff --git a/package.json b/package.json index 53af9910b..ee087a8d8 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "copy-webpack-plugin": "^4.0.0", "css-loader": "^0.25.0", "dialog-polyfill": "^0.4.4", + "enzyme": "^2.6.0", "eslint": "^3.12.1", "eslint-config-postcss": "^2.0.2", "eslint-config-standard": "^6.2.1", @@ -122,6 +123,7 @@ "precss": "^1.4.0", "pym.js": "^1.1.1", "react": "15.3.2", + "react-addons-test-utils": "^15.3.1", "react-dom": "15.3.2", "react-linkify": "^0.1.3", "react-mdl": "^1.7.2", diff --git a/tests/client/coral-plugin-history/Comment.spec.js b/tests/client/coral-plugin-history/Comment.spec.js new file mode 100644 index 000000000..3f871cf0a --- /dev/null +++ b/tests/client/coral-plugin-history/Comment.spec.js @@ -0,0 +1,31 @@ +import React from 'react'; +import {shallow, mount} from 'enzyme'; +import {expect} from 'chai'; +import Comment from '../Comment'; + +describe('coral-plugin-history/Comment', () => { + let render; + beforeEach(() => { + + const comment = {body: 'this is a comment'}; + const asset = {url: 'https://google.com'}; + + render = shallow(); + }); + + it('should render the provided comment body', () => { + const wrapper = mount(); + expect(wrapper.find('.myCommentBody')).to.have.length(1); + expect(wrapper.find('.myCommentBody').text()).to.equal('this is a comment'); + }); + + it('should render the asset url as a link', () => { + const wrapper = mount(); + expect(wrapper.find('.myCommentAnchor')).to.have.length(1); + expect(wrapper.find('.myCommentAnchor').text()).to.equal('https://google.com'); + }); + + it('should render the comment with styles', () => { + expect(render.props().style).to.be.defined; + }); +}); From eac53a58b41853c6f12767bb7a024d1491f5fe06 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 13:22:01 -0700 Subject: [PATCH 11/18] add jsdom. tests pass --- client/coral-plugin-history/CommentHistory.js | 18 ++-- package.json | 6 +- .../coral-plugin-history/Comment.spec.js | 15 ++-- .../CommentHistory.spec.js | 24 ++++++ tests/helpers/browser.js | 48 +++++++++++ tests/helpers/index.test.html | 84 +++++++++++++++++++ 6 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 tests/client/coral-plugin-history/CommentHistory.spec.js create mode 100644 tests/helpers/browser.js create mode 100644 tests/helpers/index.test.html diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index b937e87e9..44d06bc91 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -4,15 +4,17 @@ import styles from './CommentHistory.css'; const CommentHistory = props => { return ( -
+

All Comments

- {props.comments.map((comment, i) => { - const asset = props.assets.find(asset => asset.id === comment.asset_id); - return ; - })} +
+ {props.comments.map((comment, i) => { + const asset = props.assets.find(asset => asset.id === comment.asset_id); + return ; + })} +
); }; diff --git a/package.json b/package.json index 74d4eef10..4ec24a27a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build-watch": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.config.dev.js --watch", "lint": "./node_modules/.bin/eslint bin/* .", "lint-fix": "./node_modules/.bin/eslint bin/* . --fix", - "test": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register --recursive tests", + "test": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register tests/helpers/*.js --require ignore-styles --recursive tests", "test-watch": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register --recursive -w tests", "pree2e": "NODE_ENV=test ./scripts/pree2e.sh", "e2e": "NODE_ENV=test ./node_modules/.bin/nightwatch", @@ -106,8 +106,10 @@ "exports-loader": "^0.6.3", "fetch-mock": "^5.5.0", "hammerjs": "^2.0.8", + "ignore-styles": "^5.0.1", "immutable": "^3.8.1", "imports-loader": "^0.6.5", + "jsdom": "^9.8.3", "json-loader": "^0.5.4", "keymaster": "^1.6.2", "material-design-lite": "^1.2.1", @@ -122,7 +124,7 @@ "precss": "^1.4.0", "pym.js": "^1.1.1", "react": "15.3.2", - "react-addons-test-utils": "^15.3.1", + "react-addons-test-utils": "15.3.2", "react-dom": "15.3.2", "react-linkify": "^0.1.3", "react-mdl": "^1.7.2", diff --git a/tests/client/coral-plugin-history/Comment.spec.js b/tests/client/coral-plugin-history/Comment.spec.js index 3f871cf0a..7d49630d6 100644 --- a/tests/client/coral-plugin-history/Comment.spec.js +++ b/tests/client/coral-plugin-history/Comment.spec.js @@ -1,26 +1,25 @@ import React from 'react'; import {shallow, mount} from 'enzyme'; import {expect} from 'chai'; -import Comment from '../Comment'; +import Comment from '../../../client/coral-plugin-history/Comment'; describe('coral-plugin-history/Comment', () => { let render; + const comment = {body: 'this is a comment'}; + const asset = {url: 'https://google.com'}; + beforeEach(() => { - - const comment = {body: 'this is a comment'}; - const asset = {url: 'https://google.com'}; - - render = shallow(); + render = shallow(); }); it('should render the provided comment body', () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('.myCommentBody')).to.have.length(1); expect(wrapper.find('.myCommentBody').text()).to.equal('this is a comment'); }); it('should render the asset url as a link', () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('.myCommentAnchor')).to.have.length(1); expect(wrapper.find('.myCommentAnchor').text()).to.equal('https://google.com'); }); diff --git a/tests/client/coral-plugin-history/CommentHistory.spec.js b/tests/client/coral-plugin-history/CommentHistory.spec.js new file mode 100644 index 000000000..f34361985 --- /dev/null +++ b/tests/client/coral-plugin-history/CommentHistory.spec.js @@ -0,0 +1,24 @@ +import React from 'react'; +import {shallow, mount} from 'enzyme'; +import {expect} from 'chai'; +import CommentHistory from '../../../client/coral-plugin-history/CommentHistory'; + +describe('coral-plugin-history/CommentHistory', () => { + let render; + const comments = [{"body":"a comment or something","status_history":[{"type":"premod","created_at":"2016-12-09T01:40:53.327Z","assigned_by":null},{"created_at":"2016-12-09T22:52:44.888Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-09T01:40:53.360Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T22:52:44.893Z","id":"3962c2ea-4ec4-42e4-b9bd-c571ff30f56b"},{"body":"another comment","status_history":[{"type":"premod","created_at":"2016-12-09T22:53:43.148Z","assigned_by":null}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-09T22:53:43.158Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"premod","__v":0,"updated_at":"2016-12-09T22:53:43.158Z","id":"b51e27af-bcfd-4932-91be-e3f01a4802e6"},{"body":"can I comment?","status_history":[{"type":"premod","created_at":"2016-12-13T23:23:47.123Z","assigned_by":null},{"created_at":"2016-12-13T23:23:58.487Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"cef81015-1b53-4d70-b9af-6eca680f22fc","created_at":"2016-12-13T23:23:47.131Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-13T23:23:58.493Z","id":"dc9d7be1-b911-4dc3-8e1e-400e8b8d110e"},{"body":"pre-mod comment","status_history":[{"type":"premod","created_at":"2016-12-08T21:34:56.994Z","assigned_by":null},{"created_at":"2016-12-08T21:38:04.961Z","type":"rejected","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T21:34:56.997Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"rejected","__v":0,"updated_at":"2016-12-08T21:38:04.965Z","id":"6f02af16-a8f8-4ead-80ea-0d48824eb74d"},{"body":"a flagged commetn","status_history":[{"type":"premod","created_at":"2016-12-08T21:38:26.342Z","assigned_by":null},{"created_at":"2016-12-09T23:47:27.009Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T21:38:26.344Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T23:47:27.018Z","id":"784c5f91-36b9-4bda-b4ca-a114cef2c9f0"},{"body":"a post mod comment","status_history":[{"type":"premod","created_at":"2016-12-08T22:19:05.870Z","assigned_by":null},{"created_at":"2016-12-09T23:26:41.427Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T22:19:05.874Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T23:26:41.450Z","id":"e8b86039-f850-4e53-bd9d-f8c9186a9637"},{"body":"an actual post-mod comment here","status_history":[],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T22:20:11.147Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":null,"__v":0,"updated_at":"2016-12-08T22:20:11.147Z","id":"cff1a318-50c6-431e-9a63-de7a7b7136bf"}]; + const assets = [{"settings":null,"created_at":"2016-12-06T21:36:09.302Z","url":"localhost:3000/","scraped":null,"status":"open","updated_at":"2016-12-08T02:11:15.943Z","_id":"58472f499e775a38f23d5da0","type":"article","closedMessage":null,"id":"7302e637-f884-47c0-9723-02cc10a18617","closedAt":null},{"settings":null,"created_at":"2016-12-07T02:25:31.983Z","url":"http://localhost:3000/","scraped":null,"status":"open","updated_at":"2016-12-13T22:58:36.061Z","_id":"5847731b9e775a38f23d5da1","type":"article","closedMessage":null,"id":"96fddf96-7c83-4008-80ad-50091997d006","closedAt":null},{"settings":null,"created_at":"2016-12-12T19:04:05.770Z","url":"http://localhost:3000/embed/stream","scraped":null,"updated_at":"2016-12-14T20:13:21.934Z","_id":"584ef4a59e775a38f23d5e86","type":"article","closedMessage":null,"id":"cef81015-1b53-4d70-b9af-6eca680f22fc","closedAt":null}]; + + beforeEach(() => { + render = shallow(); + }); + + it('should render Comments as children when given comments and assets', () => { + const wrapper = mount(); + expect(wrapper.find('.commentHistory__list').children()).to.have.length(7); + }); + + it('should render with styles', () => { + expect(render.props().style).to.be.defined; + }); +}); + diff --git a/tests/helpers/browser.js b/tests/helpers/browser.js new file mode 100644 index 000000000..7d6a4f36a --- /dev/null +++ b/tests/helpers/browser.js @@ -0,0 +1,48 @@ +var jsdom = require('jsdom').jsdom; +var fs = require('fs'); + +// Storage Mock +function storageMock() { + var storage = {}; + + return { + setItem: function(key, value) { + storage[key] = value || ''; + }, + getItem: function(key) { + return storage[key] || null; + }, + removeItem: function(key) { + delete storage[key]; + }, + get length() { + return Object.keys(storage).length; + }, + key: function(i) { + var keys = Object.keys(storage); + return keys[i] || null; + } + }; +} + +global.document = jsdom(fs.readFileSync(__dirname + '/index.test.html')); +global.window = document.defaultView; + +// these lines are required for react-mdl +global.window.CustomEvent = undefined; +require('react-mdl/extra/material'); + +global.Element = global.window.Element; + +global.navigator = { + userAgent: 'node.js' +}; + +global.documentRef = document; +global.localStorage = {}; +global.sessionStorage = storageMock(); +global.XMLHttpRequest = storageMock(); + +global.Headers = function(headers) { + return headers; +}; diff --git a/tests/helpers/index.test.html b/tests/helpers/index.test.html new file mode 100644 index 000000000..4619df971 --- /dev/null +++ b/tests/helpers/index.test.html @@ -0,0 +1,84 @@ + + + + + + + Coral - (Beta) + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + From a072601c3e33387df466639f5171429e51ea8e53 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 13:35:28 -0700 Subject: [PATCH 12/18] ignore html files --- .eslintignore | 1 + .../coral-plugin-history/CommentHistory.spec.js | 4 ++-- tests/helpers/browser.js | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.eslintignore b/.eslintignore index a4865e1f6..83564d3ff 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ dist client/lib +**/*.html diff --git a/tests/client/coral-plugin-history/CommentHistory.spec.js b/tests/client/coral-plugin-history/CommentHistory.spec.js index f34361985..3b6f5ee47 100644 --- a/tests/client/coral-plugin-history/CommentHistory.spec.js +++ b/tests/client/coral-plugin-history/CommentHistory.spec.js @@ -5,8 +5,8 @@ import CommentHistory from '../../../client/coral-plugin-history/CommentHistory' describe('coral-plugin-history/CommentHistory', () => { let render; - const comments = [{"body":"a comment or something","status_history":[{"type":"premod","created_at":"2016-12-09T01:40:53.327Z","assigned_by":null},{"created_at":"2016-12-09T22:52:44.888Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-09T01:40:53.360Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T22:52:44.893Z","id":"3962c2ea-4ec4-42e4-b9bd-c571ff30f56b"},{"body":"another comment","status_history":[{"type":"premod","created_at":"2016-12-09T22:53:43.148Z","assigned_by":null}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-09T22:53:43.158Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"premod","__v":0,"updated_at":"2016-12-09T22:53:43.158Z","id":"b51e27af-bcfd-4932-91be-e3f01a4802e6"},{"body":"can I comment?","status_history":[{"type":"premod","created_at":"2016-12-13T23:23:47.123Z","assigned_by":null},{"created_at":"2016-12-13T23:23:58.487Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"cef81015-1b53-4d70-b9af-6eca680f22fc","created_at":"2016-12-13T23:23:47.131Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-13T23:23:58.493Z","id":"dc9d7be1-b911-4dc3-8e1e-400e8b8d110e"},{"body":"pre-mod comment","status_history":[{"type":"premod","created_at":"2016-12-08T21:34:56.994Z","assigned_by":null},{"created_at":"2016-12-08T21:38:04.961Z","type":"rejected","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T21:34:56.997Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"rejected","__v":0,"updated_at":"2016-12-08T21:38:04.965Z","id":"6f02af16-a8f8-4ead-80ea-0d48824eb74d"},{"body":"a flagged commetn","status_history":[{"type":"premod","created_at":"2016-12-08T21:38:26.342Z","assigned_by":null},{"created_at":"2016-12-09T23:47:27.009Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T21:38:26.344Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T23:47:27.018Z","id":"784c5f91-36b9-4bda-b4ca-a114cef2c9f0"},{"body":"a post mod comment","status_history":[{"type":"premod","created_at":"2016-12-08T22:19:05.870Z","assigned_by":null},{"created_at":"2016-12-09T23:26:41.427Z","type":"accepted","assigned_by":"92256159-1164-4f66-9970-c7f23de7e461"}],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T22:19:05.874Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":"accepted","__v":0,"updated_at":"2016-12-09T23:26:41.450Z","id":"e8b86039-f850-4e53-bd9d-f8c9186a9637"},{"body":"an actual post-mod comment here","status_history":[],"asset_id":"96fddf96-7c83-4008-80ad-50091997d006","created_at":"2016-12-08T22:20:11.147Z","author_id":"92256159-1164-4f66-9970-c7f23de7e461","status":null,"__v":0,"updated_at":"2016-12-08T22:20:11.147Z","id":"cff1a318-50c6-431e-9a63-de7a7b7136bf"}]; - const assets = [{"settings":null,"created_at":"2016-12-06T21:36:09.302Z","url":"localhost:3000/","scraped":null,"status":"open","updated_at":"2016-12-08T02:11:15.943Z","_id":"58472f499e775a38f23d5da0","type":"article","closedMessage":null,"id":"7302e637-f884-47c0-9723-02cc10a18617","closedAt":null},{"settings":null,"created_at":"2016-12-07T02:25:31.983Z","url":"http://localhost:3000/","scraped":null,"status":"open","updated_at":"2016-12-13T22:58:36.061Z","_id":"5847731b9e775a38f23d5da1","type":"article","closedMessage":null,"id":"96fddf96-7c83-4008-80ad-50091997d006","closedAt":null},{"settings":null,"created_at":"2016-12-12T19:04:05.770Z","url":"http://localhost:3000/embed/stream","scraped":null,"updated_at":"2016-12-14T20:13:21.934Z","_id":"584ef4a59e775a38f23d5e86","type":"article","closedMessage":null,"id":"cef81015-1b53-4d70-b9af-6eca680f22fc","closedAt":null}]; + const comments = [{body: 'a comment or something', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T01:40:53.327Z', 'assigned_by':null}, {'created_at':'2016-12-09T22:52:44.888Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T01:40:53.360Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T22:52:44.893Z', 'id':'3962c2ea-4ec4-42e4-b9bd-c571ff30f56b'}, {'body':'another comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T22:53:43.148Z', 'assigned_by':null}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T22:53:43.158Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'premod', '__v':0, 'updated_at':'2016-12-09T22:53:43.158Z', 'id':'b51e27af-bcfd-4932-91be-e3f01a4802e6'}, {'body':'can I comment?', 'status_history':[{'type':'premod', 'created_at':'2016-12-13T23:23:47.123Z', 'assigned_by':null}, {'created_at':'2016-12-13T23:23:58.487Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'cef81015-1b53-4d70-b9af-6eca680f22fc', 'created_at':'2016-12-13T23:23:47.131Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-13T23:23:58.493Z', 'id':'dc9d7be1-b911-4dc3-8e1e-400e8b8d110e'}, {'body':'pre-mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:34:56.994Z', 'assigned_by':null}, {'created_at':'2016-12-08T21:38:04.961Z', 'type':'rejected', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:34:56.997Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'rejected', '__v':0, 'updated_at':'2016-12-08T21:38:04.965Z', 'id':'6f02af16-a8f8-4ead-80ea-0d48824eb74d'}, {'body':'a flagged commetn', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:38:26.342Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:47:27.009Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:38:26.344Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:47:27.018Z', 'id':'784c5f91-36b9-4bda-b4ca-a114cef2c9f0'}, {'body':'a post mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T22:19:05.870Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:26:41.427Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:19:05.874Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:26:41.450Z', 'id':'e8b86039-f850-4e53-bd9d-f8c9186a9637'}, {'body':'an actual post-mod comment here', 'status_history':[], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:20:11.147Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':null, '__v':0, 'updated_at':'2016-12-08T22:20:11.147Z', 'id':'cff1a318-50c6-431e-9a63-de7a7b7136bf'}]; + const assets = [{'settings': null, 'created_at':'2016-12-06T21:36:09.302Z', 'url':'localhost:3000/', 'scraped':null, 'status':'open', 'updated_at':'2016-12-08T02:11:15.943Z', '_id':'58472f499e775a38f23d5da0', 'type':'article', 'closedMessage':null, 'id':'7302e637-f884-47c0-9723-02cc10a18617', 'closedAt':null}, {'settings':null, 'created_at':'2016-12-07T02:25:31.983Z', 'url':'http://localhost:3000/', 'scraped':null, 'status':'open', 'updated_at':'2016-12-13T22:58:36.061Z', '_id':'5847731b9e775a38f23d5da1', 'type':'article', 'closedMessage':null, 'id':'96fddf96-7c83-4008-80ad-50091997d006', 'closedAt':null}, {'settings':null, 'created_at':'2016-12-12T19:04:05.770Z', 'url':'http://localhost:3000/embed/stream', 'scraped':null, 'updated_at':'2016-12-14T20:13:21.934Z', '_id':'584ef4a59e775a38f23d5e86', 'type':'article', 'closedMessage':null, 'id':'cef81015-1b53-4d70-b9af-6eca680f22fc', 'closedAt':null}]; beforeEach(() => { render = shallow(); diff --git a/tests/helpers/browser.js b/tests/helpers/browser.js index 7d6a4f36a..0714b83af 100644 --- a/tests/helpers/browser.js +++ b/tests/helpers/browser.js @@ -1,9 +1,10 @@ -var jsdom = require('jsdom').jsdom; -var fs = require('fs'); +const jsdom = require('jsdom').jsdom; +const fs = require('fs'); +const path = require('path'); // Storage Mock function storageMock() { - var storage = {}; + const storage = {}; return { setItem: function(key, value) { @@ -19,13 +20,13 @@ function storageMock() { return Object.keys(storage).length; }, key: function(i) { - var keys = Object.keys(storage); + const keys = Object.keys(storage); return keys[i] || null; } }; } -global.document = jsdom(fs.readFileSync(__dirname + '/index.test.html')); +global.document = jsdom(fs.readFileSync(path.resolve(__dirname, '/index.test.html'))); global.window = document.defaultView; // these lines are required for react-mdl From a6fdd2e46bf58f9d0a13525136f7c73d2f6eaebf Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 13:53:07 -0700 Subject: [PATCH 13/18] remove redundant endpoint --- client/coral-framework/actions/user.js | 4 ++-- routes/api/comments/index.js | 17 ++++------------- tests/helpers/browser.js | 2 +- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index 5cbd04792..e1d12b3e3 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -31,8 +31,8 @@ export const saveBio = (user_id, formData) => dispatch => { export const fetchCommentsByUserId = userId => { return (dispatch) => { dispatch({type: actions.REQUEST_COMMENTS_BY_USER}); - return coralApi(`/comments/user/${userId}`) - .then(comments => { + return coralApi(`/comments?user_id${userId}`) + .then(({comments}) => { comments.forEach(comment => { dispatch(addItem(comment, 'comments')); }); diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index 534b14366..fca2bcb31 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -14,7 +14,8 @@ router.get('/', authorization.needed('admin'), (req, res, next) => { const { status = null, action_type = null, - asset_id = null + asset_id = null, + user_id = null } = req.query; /** @@ -32,6 +33,8 @@ router.get('/', authorization.needed('admin'), (req, res, next) => { if (status) { query = assetIDWrap(Comment.findByStatus(status === 'new' ? null : status)); + } else if (user_id) { + query = Comment.findByUserId(user_id); } else if (action_type) { query = Comment .findIdsByActionType(action_type) @@ -121,18 +124,6 @@ router.post('/', wordlist.filter('body'), (req, res, next) => { }); }); -router.get('/user/:user_id', (req, res, next) => { - // how to only get YOUR comments? - Comment.findByUserId(req.params.user_id) - .then(comments => { - res.json(comments); - }) - .catch(error => { - error.status = 500; - next(error); - }); -}); - router.get('/:comment_id', authorization.needed('admin'), (req, res, next) => { Comment .findById(req.params.comment_id) diff --git a/tests/helpers/browser.js b/tests/helpers/browser.js index 0714b83af..3fd54e0fe 100644 --- a/tests/helpers/browser.js +++ b/tests/helpers/browser.js @@ -26,7 +26,7 @@ function storageMock() { }; } -global.document = jsdom(fs.readFileSync(path.resolve(__dirname, '/index.test.html'))); +global.document = jsdom(fs.readFileSync(path.resolve(__dirname, 'index.test.html'))); global.window = document.defaultView; // these lines are required for react-mdl From 601010f1f9662d3dcde8f0b57129144454002f0c Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 13:58:28 -0700 Subject: [PATCH 14/18] update constant names --- client/coral-framework/actions/assets.js | 12 ++++++------ client/coral-framework/actions/user.js | 6 +++--- client/coral-framework/constants/user.js | 6 +++--- client/coral-framework/reducers/user.js | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/client/coral-framework/actions/assets.js b/client/coral-framework/actions/assets.js index 5de9a86d5..e5073b63c 100644 --- a/client/coral-framework/actions/assets.js +++ b/client/coral-framework/actions/assets.js @@ -1,19 +1,19 @@ import coralApi from '../helpers/response'; import {addItem} from './items'; -export const FETCH_MULTIPLE_ASSETS = 'FETCH_MULTIPLE_ASSETS'; -export const RECEIVE_MULTIPLE_ASSETS = 'RECEIVE_MULTIPLE_ASSETS'; -export const FAILURE_MULTIPLE_ASSSETS = 'FAILURE_MULTIPLE_ASSSETS'; +export const MULTIPLE_ASSETS_REQUEST = 'MULTIPLE_ASSETS_REQUEST'; +export const MULTIPLE_ASSETS_SUCCESS = 'MULTIPLE_ASSETS_SUCCESS'; +export const MULTIPLE_ASSSETS_FAILURE = 'MULTIPLE_ASSSETS_FAILURE'; export const fetchMulitpleAssets = ids => { return dispatch => { - dispatch({type: FETCH_MULTIPLE_ASSETS}); + dispatch({type: MULTIPLE_ASSETS_REQUEST}); coralApi(`/assets/multi?ids=${encodeURIComponent(ids.join(','))}`) .then(assets => { assets.forEach(asset => dispatch(addItem(asset, 'assets'))); - dispatch({type: RECEIVE_MULTIPLE_ASSETS, assets: assets.map(asset => asset.id)}); + dispatch({type: MULTIPLE_ASSETS_SUCCESS, assets: assets.map(asset => asset.id)}); }) - .catch(error => dispatch({type: FAILURE_MULTIPLE_ASSSETS, error})); + .catch(error => dispatch({type: MULTIPLE_ASSSETS_FAILURE, error})); }; }; diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index e1d12b3e3..b279ad0a4 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -30,19 +30,19 @@ export const saveBio = (user_id, formData) => dispatch => { */ export const fetchCommentsByUserId = userId => { return (dispatch) => { - dispatch({type: actions.REQUEST_COMMENTS_BY_USER}); + dispatch({type: actions.COMMENTS_BY_USER_REQUEST}); return coralApi(`/comments?user_id${userId}`) .then(({comments}) => { comments.forEach(comment => { dispatch(addItem(comment, 'comments')); }); - dispatch({type: actions.RECEIVE_COMMENTS_BY_USER, comments: comments.map(comment => comment.id)}); + dispatch({type: actions.COMMENTS_BY_USER_SUCCESS, comments: comments.map(comment => comment.id)}); }) .catch(error => { console.error(error.stack); console.error('FAILURE_COMMENTS_BY_USER', error); - dispatch({type: actions.FAILURE_COMMENTS_BY_USER, error}); + dispatch({type: actions.COMMENTS_BY_USER_FAILURE, error}); }); }; }; diff --git a/client/coral-framework/constants/user.js b/client/coral-framework/constants/user.js index ce9af61a2..6e09726d3 100644 --- a/client/coral-framework/constants/user.js +++ b/client/coral-framework/constants/user.js @@ -1,6 +1,6 @@ export const SAVE_BIO_REQUEST = 'SAVE_BIO_REQUEST'; export const SAVE_BIO_SUCCESS = 'SAVE_BIO_SUCCESS'; export const SAVE_BIO_FAILURE = 'SAVE_BIO_FAILURE'; -export const REQUEST_COMMENTS_BY_USER = 'REQUEST_COMMENTS_BY_USER'; -export const RECEIVE_COMMENTS_BY_USER = 'RECEIVE_COMMENTS_BY_USER'; -export const FAILURE_COMMENTS_BY_USER = 'FAILURE_COMMENTS_BY_USER'; +export const COMMENTS_BY_USER_REQUEST = 'COMMENTS_BY_USER_REQUEST'; +export const COMMENTS_BY_USER_SUCCESS = 'COMMENTS_BY_USER_SUCCESS'; +export const COMMENTS_BY_USER_FAILURE = 'COMMENTS_BY_USER_FAILURE'; diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index c4d061b42..5c064e07a 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -33,9 +33,9 @@ export default function user (state = initialState, action) { case actions.SAVE_BIO_SUCCESS: return state .set('settings', action.settings); - case actions.RECEIVE_COMMENTS_BY_USER: + case actions.COMMENTS_BY_USER_SUCCESS: return state.set('myComments', fromJS(action.comments)); - case assetActions.RECEIVE_MULTIPLE_ASSETS: + case assetActions.MULTIPLE_ASSETS_SUCCESS: return state.set('myAssets', fromJS(action.assets)); default : return state; From 29b6fc08cd166ff0c1577c569f2e24d1cd7aeda1 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Wed, 14 Dec 2016 14:41:54 -0700 Subject: [PATCH 15/18] hydrate the assets in the comment response --- client/coral-framework/actions/assets.js | 11 ++++------- client/coral-framework/actions/user.js | 10 ++++++---- client/coral-framework/constants/assets.js | 3 +++ client/coral-framework/reducers/user.js | 2 +- client/coral-settings/containers/SettingsContainer.js | 8 +------- routes/api/comments/index.js | 4 +++- 6 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 client/coral-framework/constants/assets.js diff --git a/client/coral-framework/actions/assets.js b/client/coral-framework/actions/assets.js index e5073b63c..f778831b7 100644 --- a/client/coral-framework/actions/assets.js +++ b/client/coral-framework/actions/assets.js @@ -1,19 +1,16 @@ +import * as actions from '../constants/assets'; import coralApi from '../helpers/response'; import {addItem} from './items'; -export const MULTIPLE_ASSETS_REQUEST = 'MULTIPLE_ASSETS_REQUEST'; -export const MULTIPLE_ASSETS_SUCCESS = 'MULTIPLE_ASSETS_SUCCESS'; -export const MULTIPLE_ASSSETS_FAILURE = 'MULTIPLE_ASSSETS_FAILURE'; - export const fetchMulitpleAssets = ids => { return dispatch => { - dispatch({type: MULTIPLE_ASSETS_REQUEST}); + dispatch({type: actions.MULTIPLE_ASSETS_REQUEST}); coralApi(`/assets/multi?ids=${encodeURIComponent(ids.join(','))}`) .then(assets => { assets.forEach(asset => dispatch(addItem(asset, 'assets'))); - dispatch({type: MULTIPLE_ASSETS_SUCCESS, assets: assets.map(asset => asset.id)}); + dispatch({type: actions.MULTIPLE_ASSETS_SUCCESS, assets: assets.map(asset => asset.id)}); }) - .catch(error => dispatch({type: MULTIPLE_ASSSETS_FAILURE, error})); + .catch(error => dispatch({type: actions.MULTIPLE_ASSSETS_FAILURE, error})); }; }; diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index b279ad0a4..69f4882bb 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,4 +1,5 @@ import * as actions from '../constants/user'; +import * as assetActions from '../constants/assets'; import {addNotification} from '../actions/notification'; import {addItem} from '../actions/items'; import coralApi from '../helpers/response'; @@ -32,12 +33,13 @@ export const fetchCommentsByUserId = userId => { return (dispatch) => { dispatch({type: actions.COMMENTS_BY_USER_REQUEST}); return coralApi(`/comments?user_id${userId}`) - .then(({comments}) => { - comments.forEach(comment => { - dispatch(addItem(comment, 'comments')); - }); + .then(({comments, assets}) => { + comments.forEach(comment => dispatch(addItem(comment, 'comments'))); + + assets.forEach(asset => dispatch(addItem(asset, 'assets'))); dispatch({type: actions.COMMENTS_BY_USER_SUCCESS, comments: comments.map(comment => comment.id)}); + dispatch({type: assetActions.MULTIPLE_ASSETS_SUCCESS, assets: assets.map(asset => asset.id)}); }) .catch(error => { console.error(error.stack); diff --git a/client/coral-framework/constants/assets.js b/client/coral-framework/constants/assets.js new file mode 100644 index 000000000..0224e0945 --- /dev/null +++ b/client/coral-framework/constants/assets.js @@ -0,0 +1,3 @@ +export const MULTIPLE_ASSETS_REQUEST = 'MULTIPLE_ASSETS_REQUEST'; +export const MULTIPLE_ASSETS_SUCCESS = 'MULTIPLE_ASSETS_SUCCESS'; +export const MULTIPLE_ASSSETS_FAILURE = 'MULTIPLE_ASSSETS_FAILURE'; \ No newline at end of file diff --git a/client/coral-framework/reducers/user.js b/client/coral-framework/reducers/user.js index 5c064e07a..6e9f3529c 100644 --- a/client/coral-framework/reducers/user.js +++ b/client/coral-framework/reducers/user.js @@ -1,7 +1,7 @@ import {Map, fromJS} from 'immutable'; import * as authActions from '../constants/auth'; import * as actions from '../constants/user'; -import * as assetActions from '../actions/assets'; +import * as assetActions from '../constants/assets'; const initialState = Map({ displayName: '', diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 9d535d595..0981bafe3 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -23,13 +23,7 @@ class SignInContainer extends Component { componentWillMount () { // Fetch commentHistory - this.props.fetchCommentsByUserId(this.props.userData.id) - .then(() => { - const assetIds = this.props.user.myComments - .map(id => this.props.items.comments[id]) - .map(comment => comment.asset_id); - this.props.fetchMulitpleAssets(assetIds); - }); + this.props.fetchCommentsByUserId(this.props.userData.id); } handleTabChange(tab) { diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index fca2bcb31..8f21a9488 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -50,13 +50,15 @@ router.get('/', authorization.needed('admin'), (req, res, next) => { query.then((comments) => { return Promise.all([ comments, + Asset.findMultipleById(comments.map(comment => comment.asset_id)), User.findByIdArray(_.uniq(comments.map((comment) => comment.author_id))), Action.getActionSummariesFromComments(asset_id, comments, req.user ? req.user.id : false) ]); }) - .then(([comments, users, actions])=> + .then(([comments, assets, users, actions]) => res.status(200).json({ comments, + assets, users, actions })) From 0892996be07c634f0e8085983fd25d076251a054 Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Thu, 15 Dec 2016 10:41:34 -0700 Subject: [PATCH 16/18] remove this route until someone is actually using it --- routes/api/assets/index.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/routes/api/assets/index.js b/routes/api/assets/index.js index 20fbf1630..5aa9b8cc6 100644 --- a/routes/api/assets/index.js +++ b/routes/api/assets/index.js @@ -40,20 +40,6 @@ router.get('/', (req, res, next) => { }); -// get multiple assets with a comma-separated list of asset ids -router.get('/multi', (req, res, next) => { - const assetIds = req.query.ids.split(','); - - Asset.findMultipleById(assetIds) - .then(assets => { - res.json(assets); - }) - .catch(error => { - error.status = 500; - next(error); - }); -}); - // Get an asset by id. router.get('/:asset_id', (req, res, next) => { From d599a65c05552521be0fa5c5907038b92cb7e76d Mon Sep 17 00:00:00 2001 From: Riley Davis Date: Thu, 15 Dec 2016 10:44:20 -0700 Subject: [PATCH 17/18] file not being used anywhere --- client/coral-framework/actions/assets.js | 16 ---------------- .../containers/SettingsContainer.js | 4 +--- 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 client/coral-framework/actions/assets.js diff --git a/client/coral-framework/actions/assets.js b/client/coral-framework/actions/assets.js deleted file mode 100644 index f778831b7..000000000 --- a/client/coral-framework/actions/assets.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as actions from '../constants/assets'; -import coralApi from '../helpers/response'; -import {addItem} from './items'; - -export const fetchMulitpleAssets = ids => { - return dispatch => { - dispatch({type: actions.MULTIPLE_ASSETS_REQUEST}); - - coralApi(`/assets/multi?ids=${encodeURIComponent(ids.join(','))}`) - .then(assets => { - assets.forEach(asset => dispatch(addItem(asset, 'assets'))); - dispatch({type: actions.MULTIPLE_ASSETS_SUCCESS, assets: assets.map(asset => asset.id)}); - }) - .catch(error => dispatch({type: actions.MULTIPLE_ASSSETS_FAILURE, error})); - }; -}; diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 0981bafe3..8f1b32687 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -2,7 +2,6 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {saveBio, fetchCommentsByUserId} from 'coral-framework/actions/user'; -import {fetchMulitpleAssets} from 'coral-framework/actions/assets'; import BioContainer from './BioContainer'; import NotLoggedIn from '../components/NotLoggedIn'; @@ -66,8 +65,7 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)), - fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)), - fetchMulitpleAssets: assetIds => dispatch(fetchMulitpleAssets(assetIds)) + fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)) }); export default connect( From fcb107aefeebaa5709d2762a182edba9c5602918 Mon Sep 17 00:00:00 2001 From: Dan Zajdband Date: Thu, 15 Dec 2016 15:50:19 -0500 Subject: [PATCH 18/18] Global comment stream close setting (#171) * Added basic ui * Added functionality * Linted code * using named params instead of props * lint * fixed missing merge --- .../containers/Configure/CommentSettings.js | 59 +++++++++++++++++++ .../src/containers/Configure/Configure.css | 5 ++ client/coral-admin/src/translations.json | 9 +++ .../containers/ConfigureStreamContainer.js | 11 ++++ 4 files changed, 84 insertions(+) diff --git a/client/coral-admin/src/containers/Configure/CommentSettings.js b/client/coral-admin/src/containers/Configure/CommentSettings.js index 3a6a796e4..5819076c6 100644 --- a/client/coral-admin/src/containers/Configure/CommentSettings.js +++ b/client/coral-admin/src/containers/Configure/CommentSettings.js @@ -1,4 +1,5 @@ import React from 'react'; +import {SelectField, Option} from 'react-mdl-selectfield'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../../translations.json'; import styles from './Configure.css'; @@ -12,6 +13,12 @@ import { Icon } from 'react-mdl'; +const TIMESTAMPS = { + weeks: 60 * 60 * 24 * 7, + days: 60 * 60 * 24, + hours: 60 * 60 +}; + const updateCharCountEnable = (updateSettings, charCountChecked) => () => { const charCountEnable = !charCountChecked; updateSettings({charCountEnable}); @@ -47,6 +54,21 @@ const updateClosedMessage = (updateSettings) => (event) => { updateSettings({closedMessage}); }; +// If we are changing the measure we need to recalculate using the old amount +// Same thing if we are just changing the amount +const updateClosedTimeout = (updateSettings, ts, isMeasure) => (event) => { + if (isMeasure) { + const amount = getTimeoutAmount(ts); + const closedTimeout = amount * TIMESTAMPS[event]; + updateSettings({closedTimeout}); + } else { + const val = event.target.value; + const measure = getTimeoutMeasure(ts); + const closedTimeout = val * TIMESTAMPS[measure]; + updateSettings({closedTimeout}); + } +}; + const CommentSettings = ({updateSettings, settingsError, settings, errors}) => @@ -110,6 +132,27 @@ const CommentSettings = ({updateSettings, settingsError, settings, errors}) => < rows={3}/> + + + {lang.t('configure.close-after')} +
+ +
+ + + + + +
+
+
{lang.t('configure.closed-comments-desc')} @@ -124,4 +167,20 @@ const CommentSettings = ({updateSettings, settingsError, settings, errors}) => < export default CommentSettings; +// To see if we are talking about weeks, days or hours +// We talk the remainder of the division and see if it's 0 +const getTimeoutMeasure = ts => { + if (ts % TIMESTAMPS['weeks'] === 0) { + return 'weeks'; + } else if (ts % TIMESTAMPS['days'] === 0) { + return 'days'; + } else if (ts % TIMESTAMPS['hours'] === 0) { + return 'hours'; + } +}; + +// Dividing the amount by it's measure (hours, days, weeks) we +// obtain the amount of time +const getTimeoutAmount = ts => ts / TIMESTAMPS[getTimeoutMeasure(ts)]; + const lang = new I18n(translations); diff --git a/client/coral-admin/src/containers/Configure/Configure.css b/client/coral-admin/src/containers/Configure/Configure.css index 667bb4744..c0646c9e2 100644 --- a/client/coral-admin/src/containers/Configure/Configure.css +++ b/client/coral-admin/src/containers/Configure/Configure.css @@ -63,6 +63,11 @@ display: block; } +.configTimeoutSelect { + display: inline-block; + margin-left: 20px; +} + .charCountTexfield { width: 4em; padding: 0px; diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index e462f7567..ef00f1cf8 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -57,6 +57,10 @@ "community": "Community", "closed-comments-desc": "Write a message for closed threads", "closed-comments-label": "Write a message...", + "hours": "Hours", + "days": "Days", + "weeks": "Weeks", + "close-after": "Close comments after", "comment-count-header": "Limit Comment Length", "comment-count-text-pre": "Comments will be limited to ", "comment-count-text-post": " characters.", @@ -117,6 +121,11 @@ "community": "Comunidad", "closed-comments-desc": "Escribe un mensaje para cuando los comentarios se encuentran cerrados", "closed-comments-label": "Escribe un mensaje...", + "never": "Nunca", + "hours": "Horas", + "days": "Días", + "weeks": "Semanas", + "close-after": "Cerrar comentarios luego de", "comment-count-header": "Limitar el largo del comentario", "comment-count-text-pre": "El largo de comentarios será ", "comment-count-text-post": " caracteres", diff --git a/client/coral-configure/containers/ConfigureStreamContainer.js b/client/coral-configure/containers/ConfigureStreamContainer.js index d4b71f4e5..cb8bf0012 100644 --- a/client/coral-configure/containers/ConfigureStreamContainer.js +++ b/client/coral-configure/containers/ConfigureStreamContainer.js @@ -1,11 +1,14 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; +import {I18n} from '../../coral-framework'; import {updateOpenStatus, updateConfiguration} from '../../coral-framework/actions/config'; import CloseCommentsInfo from '../components/CloseCommentsInfo'; import ConfigureCommentStream from '../components/ConfigureCommentStream'; +const lang = new I18n(); + class ConfigureStreamContainer extends Component { constructor (props) { super(props); @@ -47,8 +50,15 @@ class ConfigureStreamContainer extends Component { this.props.updateStatus(this.props.config.status === 'open' ? 'closed' : 'open'); } + getClosedIn () { + const {config} = this.props; + const {created_at, closedTimeout} = config; + return lang.timeago(new Date(created_at).getTime() + (1000 * closedTimeout)); + } + render () { const {status} = this.props; + return (

{status === 'open' ? 'Close' : 'Open'} Comment Stream

+ {status === 'open' ?

The comment stream will close in {this.getClosedIn()}.

: ''}