From 26b7d48f1fb3981babfa5be0501340e94a4616c8 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 24 Jan 2017 17:06:40 -0700 Subject: [PATCH 01/28] Comment ordering fixed, banned users can't do anything --- client/coral-framework/helpers/validate.js | 2 +- graph/index.js | 4 ++ graph/loaders/comments.js | 48 +++++++++++++--------- graph/mutators/action.js | 2 +- graph/mutators/comment.js | 2 +- graph/mutators/user.js | 2 +- models/user.js | 27 ++++++++++++ 7 files changed, 64 insertions(+), 23 deletions(-) diff --git a/client/coral-framework/helpers/validate.js b/client/coral-framework/helpers/validate.js index 6e5db3662..b267fb7c0 100644 --- a/client/coral-framework/helpers/validate.js +++ b/client/coral-framework/helpers/validate.js @@ -1,5 +1,5 @@ export default { - email: email => (/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)), + email: email => (/^([A-Za-z0-9_\-\.\+])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(email)), password: pass => (/^(?=.{8,}).*$/.test(pass)), confirmPassword: () => true, displayName: displayName => (/^[a-zA-Z0-9_]+$/.test(displayName)) diff --git a/graph/index.js b/graph/index.js index 7fddce2eb..f8695afbb 100644 --- a/graph/index.js +++ b/graph/index.js @@ -12,3 +12,7 @@ module.exports = { context: new Context(req) }) }; + +process.on('unhandledRejection', (err) => { + console.error(err); +}); diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 35463dfd0..77c5d172c 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -7,31 +7,41 @@ const CommentModel = require('../../models/comment'); const CommentsService = require('../../services/comments'); /** - * Retrieves comments by an array of asset id's. + * Retrieves comments by an array of asset id's, results are returned in reverse + * chronological order. * @param {Array} ids array of ids to lookup */ -const genCommentsByAssetID = (context, ids) => CommentModel.find({ - asset_id: { - $in: ids - }, - parent_id: null, - status: { - $in: [null, 'ACCEPTED'] - } -}).then(util.arrayJoinBy(ids, 'asset_id')); +const genCommentsByAssetID = (context, ids) => { + return CommentModel.find({ + asset_id: { + $in: ids + }, + parent_id: null, + status: { + $in: [null, 'ACCEPTED'] + } + }) + .sort({created_at: -1}) + .then(util.arrayJoinBy(ids, 'asset_id')); +}; /** - * Retrieves comments by an array of parent ids. + * Retrieves comments by an array of parent ids, results are returned in + * chronological order. * @param {Array} ids array of ids to lookup */ -const genCommentsByParentID = (context, ids) => CommentModel.find({ - parent_id: { - $in: ids - }, - status: { - $in: [null, 'ACCEPTED'] - } -}).then(util.arrayJoinBy(ids, 'parent_id')); +const genCommentsByParentID = (context, ids) => { + return CommentModel.find({ + parent_id: { + $in: ids + }, + status: { + $in: [null, 'ACCEPTED'] + } + }) + .sort({created_at: 1}) + .then(util.arrayJoinBy(ids, 'parent_id')); +}; const getCommentsByStatusAndAssetID = (context, {status = null, asset_id = null}) => { diff --git a/graph/mutators/action.js b/graph/mutators/action.js index cf80c496d..ab51c9a20 100644 --- a/graph/mutators/action.js +++ b/graph/mutators/action.js @@ -37,7 +37,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:createAction', 'mutation:deleteAction')) { return { Action: { create: (action) => createAction(context, action), diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 6bd7cea26..cc62e48df 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -144,7 +144,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:createComment')) { return { Comment: { create: (comment) => createPublicComment(context, comment) diff --git a/graph/mutators/user.js b/graph/mutators/user.js index 22837caae..96049bbab 100644 --- a/graph/mutators/user.js +++ b/graph/mutators/user.js @@ -15,7 +15,7 @@ module.exports = (context) => { // TODO: refactor to something that'll return an error in the event an attempt // is made to mutate state while not logged in. There's got to be a better way // to do this. - if (context.user) { + if (context.user && context.user.can('mutation:updateUserSettings')) { return { User: { updateSettings: (settings) => updateUserSettings(context, settings) diff --git a/models/user.js b/models/user.js index 0b8edf0fd..9c447fa5d 100644 --- a/models/user.js +++ b/models/user.js @@ -130,6 +130,33 @@ UserSchema.method('hasRoles', function(...roles) { }); }); +/** + * All the graph operations that are available for a user. + * @type {Array} + */ +const USER_GRAPH_OPERATIONS = [ + 'mutation:createComment', + 'mutation:createAction', + 'mutation:deleteAction', + 'mutation:updateUserSettings' +]; + +/** + * Can returns true if the user is allowed to perform a specific graph + * operation. + */ +UserSchema.method('can', function(...actions) { + if (actions.some((action) => USER_GRAPH_OPERATIONS.indexOf(action) === -1)) { + throw new Error(`invalid actions: ${actions}`); + } + + if (this.status === 'BANNED') { + return false; + } + + return true; +}); + // Create the User model. const UserModel = mongoose.model('User', UserSchema); From eb469df573f222ed0ecba36f078d2ae00f8437ab Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 24 Jan 2017 17:08:12 -0700 Subject: [PATCH 02/28] Removed debugging tool --- graph/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/graph/index.js b/graph/index.js index f8695afbb..7fddce2eb 100644 --- a/graph/index.js +++ b/graph/index.js @@ -12,7 +12,3 @@ module.exports = { context: new Context(req) }) }; - -process.on('unhandledRejection', (err) => { - console.error(err); -}); From 373f119ac244faf817e48a40b0c9cef6a1b0be3a Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 24 Jan 2017 17:22:29 -0700 Subject: [PATCH 03/28] Added more comment sorting --- graph/loaders/comments.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 77c5d172c..9a67bb621 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -62,7 +62,7 @@ const getCommentsByActionTypeAndAssetID = (context, {action_type, asset_id = nul id: { $in: actions.map((action) => action.item_id) } - }); + }).sort({created_at: 1}); if (asset_id) { comments = comments.where({asset_id}); @@ -72,11 +72,15 @@ const getCommentsByActionTypeAndAssetID = (context, {action_type, asset_id = nul }); }; -const genCommentsByAuthorID = (context, authorIDs) => CommentModel.find({ - author_id: { - $in: authorIDs - } -}).then(util.arrayJoinBy(authorIDs, 'author_id')); +const genCommentsByAuthorID = (context, authorIDs) => { + return CommentModel.find({ + author_id: { + $in: authorIDs + } + }) + .sort({created_at: -1}) + .then(util.arrayJoinBy(authorIDs, 'author_id')); +}; /** * Creates a set of loaders based on a GraphQL context. From e3b183c56583c0ffdaff8b599eedc77e73efa3ba Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 05:15:04 -0300 Subject: [PATCH 04/28] Items cleanup --- client/coral-embed-stream/src/Embed.js | 1 - .../src/graphql/queries/streamQuery.graphql | 22 -- client/coral-framework/actions/auth.js | 3 - client/coral-framework/actions/items.js | 268 ------------------ client/coral-framework/actions/user.js | 25 +- client/coral-framework/index.js | 16 +- client/coral-framework/reducers/index.js | 2 - client/coral-framework/reducers/items.js | 30 -- .../containers/SettingsContainer.js | 29 +- 9 files changed, 12 insertions(+), 384 deletions(-) delete mode 100644 client/coral-framework/actions/items.js delete mode 100644 client/coral-framework/reducers/items.js diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 5922266fa..87ab32fa3 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -181,7 +181,6 @@ class Embed extends Component { } const mapStateToProps = state => ({ - items: state.items.toJS(), notification: state.notification.toJS(), auth: state.auth.toJS(), userData: state.user.toJS() diff --git a/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql b/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql index 64bb46b47..5c5018d41 100644 --- a/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql +++ b/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql @@ -1,25 +1,3 @@ - -fragment commentView on Comment { - id - body - created_at - user { - id - name: displayName - settings { - bio - } - } - actions { - type: action_type - count - current: current_user { - id - created_at - } - } -} - query AssetQuery($asset_url: String!) { asset(url: $asset_url) { id diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index e815fd5d0..f7bce4808 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -3,7 +3,6 @@ import translations from './../translations'; const lang = new I18n(translations); import * as actions from '../constants/auth'; import coralApi, {base} from '../helpers/response'; -import {addItem} from './items'; // Dialog Actions export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset}); @@ -30,7 +29,6 @@ export const fetchSignIn = (formData) => (dispatch) => { const isAdmin = !!user.roles.filter(i => i === 'ADMIN').length; dispatch(signInSuccess(user, isAdmin)); dispatch(hideSignInDialog()); - dispatch(addItem(user, 'users')); }) .catch(() => dispatch(signInFailure(lang.t('error.emailPasswordError')))); }; @@ -59,7 +57,6 @@ export const facebookCallback = (err, data) => dispatch => { const user = JSON.parse(data); dispatch(signInFacebookSuccess(user)); dispatch(hideSignInDialog()); - dispatch(addItem(user, 'users')); } catch (err) { dispatch(signInFacebookFailure(err)); return; diff --git a/client/coral-framework/actions/items.js b/client/coral-framework/actions/items.js deleted file mode 100644 index e289e9f60..000000000 --- a/client/coral-framework/actions/items.js +++ /dev/null @@ -1,268 +0,0 @@ -import coralApi from '../helpers/response'; -import {fromJS} from 'immutable'; -import {UPDATE_CONFIG} from '../constants/config'; - -/** -* Action name constants -*/ - -export const ADD_ITEM = 'ADD_ITEM'; -export const UPDATE_ITEM = 'UPDATE_ITEM'; -export const APPEND_ITEM_ARRAY = 'APPEND_ITEM_ARRAY'; - -/** - * Action creators - */ - - /* - * Adds an item to the local store without posting it to the server - * Useful for optimistic posting, etc. - * - * @params - * item - the item to be posted - * - */ - -export const addItem = (item, item_type) => { - if (!item.id) { - console.warn('addItem called without an item id.'); - } - return { - type: ADD_ITEM, - item, - item_type, - id: item.id - }; -}; - -/* -* Updates an item in the local store without posting it to the server -* Useful for item-level toggles, etc. -* -* @params -* id - the id of the item to be posted -* property - the property to be updated -* value - the value that the property should be set to -* item_type - the type of the item being updated (users, comments, etc) -* -*/ -export const updateItem = (id, property, value, item_type) => { - return { - type: UPDATE_ITEM, - id, - property, - value, - item_type - }; -}; - -/* -* Appends data to an array in an item in the local store without posting it to the server -* Useful for adding a recently posted reply to a comment, etc. -* -* @params -* id - the id of the item to be posted -* property - the property to be updated (should be an array) -* value - the value that should be added to the array -* add_to_front - boolean that defines whether value is added at the beginning (unshift) or end (push) -* item_type - the type of the item being updated (users, comments, etc) -* -*/ -export const appendItemArray = (id, property, value, add_to_front, item_type) => { - return { - type: APPEND_ITEM_ARRAY, - id, - property, - value, - add_to_front, - item_type - }; -}; - -/* -* Get Items from Query -* Gets a set of items from a predefined query -* -* @params -* Query - a predefiend query for retreiving items -* -* @returns -* A promise resolving to a set of items -* -* @dispatches -* A set of items to the item store -*/ -export function getStream (assetUrl) { - return (dispatch) => { - return coralApi(`/stream?asset_url=${encodeURIComponent(assetUrl)}`) - .then((json) => { - - /* Add items to the store */ - 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 if (type === 'settings') { - dispatch({type: UPDATE_CONFIG, config: fromJS(json[type])}); - } else { - 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 ( - item.asset_id === assetId && - !item.parent_id) { - h.rootComments.push(item.id); - } else if ( - item.asset_id === assetId - ) { - let children = h.childComments[item.parent_id] || []; - h.childComments[item.parent_id] = children.concat(item.id); - } - return h; - }, {rootComments: [], childComments: {}}); - - dispatch(updateItem(assetId, 'comments', rels.rootComments, 'assets')); - - Object.keys(rels.childComments).forEach(key => { - dispatch(updateItem(key, 'children', rels.childComments[key].reverse(), 'comments')); - }); - - /* Hydrate actions on comments */ - json.actions.forEach(action => { - dispatch(updateItem(action.item_id, action.action_type, action.id, 'comments')); - }); - - return (json); - }); - }; -} - -/* -* Get Items Array -* Gets a set of items from an array of item ids -* -* @params -* Query - a predefiend query for retreiving items -* -* @returns -* A promise resolving to a set of items -* -* @dispatches -* A set of items to the item store -*/ - -export function getItemsArray (ids) { - return (dispatch) => { - return coralApi(`/item/${ids}`) - .then((json) => { - for (let i = 0; i < json.items.length; i++) { - dispatch(addItem(json.items[i])); - } - return json.items; - }); - }; -} - -/* -* PutItem -* Puts an item -* -* @params -* Item - the item to be put -* -* @returns -* A promise resolving to an item is -* -* @dispatches -* The newly put item to the item store -*/ - -export function postItem (item, type, id, mutate) { - console.log( - item, - type, - id, - mutate - ); - mutate({ - variables: { - asset_id: id, - body: item, - parent_id: null - } - }).then(({data}) => { - console.log('it workt'); - console.log(data); - }); - - // return (dispatch) => { - // if (id) { - // item.id = id; - // } - // return coralApi(`/${type}`, {method: 'POST', body: item}) - // .then((json) => { - // dispatch(addItem({...item, id:json.id}, type)); - // return json; - // }); - // }; -} - -/* -* PostAction -* Posts an action to an item -* -* @params -* id - the id of the item on which the action is taking place -* action - the action object. -* Must include an 'action_type' string. -* May optionally include a `metadata` object with arbitrary action data. -* user - the user performing the action -* host - the coral host -* -* @returns -* A promise resolving to null or an error -* -*/ - -export function postAction (item_id, item_type, action) { - return (dispatch) => { - return coralApi(`/${item_type}/${item_id}/actions`, {method: 'POST', body: action}) - .then((json) => { - dispatch(updateItem(action.item_id, action.action_type, action.id, item_type)); - return json; - }); - }; -} - -/* -* DeleteAction -* Deletes an action to an item -* -* @params -* id - the id of the item on which the action is taking place -* action - the name of the action -* user - the user performing the action -* host - the coral host -* -* @returns -* A promise resolving to null or an error -* -*/ - -export function deleteAction (action_id) { - return () => { - return coralApi(`/actions/${action_id}`, {method: 'DELETE'}); - }; -} diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index 021c48eeb..a9bf0546a 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,7 +1,6 @@ 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'; import I18n from 'coral-framework/modules/i18n/i18n'; @@ -30,27 +29,5 @@ export const saveBio = (user_id, formData) => dispatch => { * @returns Promise */ export const fetchCommentsByUserId = userId => { - return (dispatch, getState) => { - dispatch({type: actions.COMMENTS_BY_USER_REQUEST}); - return coralApi(`/comments?user_id=${userId}`) - .then(({comments, assets}) => { - const state = getState(); - comments.forEach(comment => dispatch(addItem(comment, 'comments'))); - assets.forEach(asset => { - const prevAsset = state.items.getIn(['assets', asset.id]); - - if (prevAsset) { - - // Include data such as hydrated comments from assets already in the system. - dispatch(addItem({...prevAsset.toJS(), ...asset}, 'assets')); - } else { - 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 => dispatch({type: actions.COMMENTS_BY_USER_FAILURE, error})); - }; + // TODO }; diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js index c21136d27..d2cd4197b 100644 --- a/client/coral-framework/index.js +++ b/client/coral-framework/index.js @@ -1,19 +1,17 @@ -import Notification from './modules/notification/Notification'; import store from './store'; -import * as itemActions from './actions/items'; +import pym from './PymConnection'; import I18n from './modules/i18n/i18n'; -import * as notificationActions from './actions/notification'; import * as authActions from './actions/auth'; import * as assetActions from './actions/asset'; -import pym from './PymConnection'; +import * as notificationActions from './actions/notification'; +import Notification from './modules/notification/Notification'; export { - Notification, - store, - itemActions, + pym, I18n, - notificationActions, + store, authActions, assetActions, - pym + Notification, + notificationActions }; diff --git a/client/coral-framework/reducers/index.js b/client/coral-framework/reducers/index.js index 8c1457bfe..720f15232 100644 --- a/client/coral-framework/reducers/index.js +++ b/client/coral-framework/reducers/index.js @@ -1,13 +1,11 @@ import auth from './auth'; import user from './user'; import asset from './asset'; -import items from './items'; import notification from './notification'; export default { auth, user, asset, - items, notification }; diff --git a/client/coral-framework/reducers/items.js b/client/coral-framework/reducers/items.js deleted file mode 100644 index 268557809..000000000 --- a/client/coral-framework/reducers/items.js +++ /dev/null @@ -1,30 +0,0 @@ -/* Items Reducer */ - -import {fromJS} from 'immutable'; -import * as actions from '../actions/items'; - -const initialState = fromJS({ - comments: {}, - users: {}, - assets: {}, - actions: {} -}); - -export default (state = initialState, action) => { - switch (action.type) { - case actions.ADD_ITEM: - return state.setIn([action.item_type, action.id], fromJS(action.item)); - case actions.UPDATE_ITEM: - 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) => { - if (action.add_to_front) { - return prop ? prop.unshift(fromJS(action.value)) : fromJS([action.value]); - } else { - return prop ? prop.push(fromJS(action.value)) : fromJS([action.value]); - } - }); - default: - return state; - } -}; diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 001190f1d..031e29dcf 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -6,7 +6,7 @@ import {saveBio, fetchCommentsByUserId} from 'coral-framework/actions/user'; import {link} from 'coral-framework/PymConnection'; import BioContainer from './BioContainer'; import NotLoggedIn from '../components/NotLoggedIn'; -import {TabBar, Tab, TabContent} from '../../coral-ui'; +import {TabBar, Tab, TabContent} from 'coral-ui'; import CommentHistory from 'coral-plugin-history/CommentHistory'; import SettingsHeader from '../components/SettingsHeader'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; @@ -26,9 +26,7 @@ class SettingsContainer extends Component { } componentWillMount () { - - // Fetch commentHistory - this.props.fetchCommentsByUserId(this.props.userData.id); + // this.props.fetchCommentsByUserId(this.props.userData.id); } handleTabChange(tab) { @@ -41,16 +39,6 @@ class SettingsContainer extends Component { const {loggedIn, userData, showSignInDialog, items, user} = this.props; const {activeTab} = this.state; - const commentsMostRecentFirst = user - .myComments.map(id => items.comments[id]) - .sort(({created_at:a}, {created_at:b}) => { - - // descending order, created_at - // js date strings can be sorted lexigraphically. - const aLessThanB = a < b ? 1 : 0; - return a > b ? -1 : aLessThanB; - }); - return ( }> @@ -59,14 +47,7 @@ class SettingsContainer extends Component { Profile Settings - { - user.myComments.length && user.myAssets.length - ? items.assets[id])} /> - :

{lang.t('user-no-comment')}

- } + My comment History
@@ -77,13 +58,11 @@ class SettingsContainer extends Component { } const mapStateToProps = state => ({ - items: state.items.toJS(), user: state.user.toJS() }); const mapDispatchToProps = dispatch => ({ - saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)), - fetchCommentsByUserId: userId => dispatch(fetchCommentsByUserId(userId)) + saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)) }); export default connect( From e993330467d9f6050c4e77c3c2fef3b70b938327 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 05:20:34 -0300 Subject: [PATCH 05/28] Adding fragments :tada: --- .../src/graphql/fragments/commentView.graphql | 20 +++++++++++++++++++ .../src/graphql/mutations/index.js | 5 +++++ .../src/graphql/mutations/postComment.graphql | 18 ----------------- 3 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 client/coral-embed-stream/src/graphql/fragments/commentView.graphql diff --git a/client/coral-embed-stream/src/graphql/fragments/commentView.graphql b/client/coral-embed-stream/src/graphql/fragments/commentView.graphql new file mode 100644 index 000000000..b54c00e67 --- /dev/null +++ b/client/coral-embed-stream/src/graphql/fragments/commentView.graphql @@ -0,0 +1,20 @@ +fragment commentView on Comment { + id + body + created_at + user { + id + name: displayName + settings { + bio + } + } + actions { + type: action_type + count + current: current_user { + id + created_at + } + } +} diff --git a/client/coral-embed-stream/src/graphql/mutations/index.js b/client/coral-embed-stream/src/graphql/mutations/index.js index 383862455..5fbfbf9d2 100644 --- a/client/coral-embed-stream/src/graphql/mutations/index.js +++ b/client/coral-embed-stream/src/graphql/mutations/index.js @@ -3,7 +3,12 @@ import POST_COMMENT from './postComment.graphql'; import POST_ACTION from './postAction.graphql'; import DELETE_ACTION from './deleteAction.graphql'; +import commentView from '../fragments/commentView.graphql'; + export const postComment = graphql(POST_COMMENT, { + options: () => ({ + fragments: [commentView] + }), props: ({mutate}) => ({ postItem: ({asset_id, body, parent_id} /* , type */ ) => { return mutate({ diff --git a/client/coral-embed-stream/src/graphql/mutations/postComment.graphql b/client/coral-embed-stream/src/graphql/mutations/postComment.graphql index a9496f1fa..46ef879ce 100644 --- a/client/coral-embed-stream/src/graphql/mutations/postComment.graphql +++ b/client/coral-embed-stream/src/graphql/mutations/postComment.graphql @@ -1,21 +1,3 @@ - - fragment commentView on Comment { - id - body - status - user { - name: displayName - } - actions { - type: action_type - count - current: current_user { - id - created_at - } - } - } - mutation CreateComment ($asset_id: ID!, $parent_id: ID, $body: String!) { createComment(asset_id:$asset_id, parent_id:$parent_id, body:$body) { ...commentView From b1848c28e40acfd66936dc273ac04f3ac14a13ee Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 07:08:55 -0300 Subject: [PATCH 06/28] Adding Missing Translations --- .../containers/SettingsContainer.js | 22 +++++++------------ client/coral-settings/translations.json | 10 +++++++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 031e29dcf..6dc2bf3b9 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -1,17 +1,15 @@ -import React, {Component} from 'react'; import {connect} from 'react-redux'; +import React, {Component} from 'react'; +import I18n from 'coral-framework/modules/i18n/i18n'; -import {saveBio, fetchCommentsByUserId} from 'coral-framework/actions/user'; +import {saveBio} from 'coral-framework/actions/user'; -import {link} from 'coral-framework/PymConnection'; import BioContainer from './BioContainer'; -import NotLoggedIn from '../components/NotLoggedIn'; import {TabBar, Tab, TabContent} from 'coral-ui'; -import CommentHistory from 'coral-plugin-history/CommentHistory'; +import NotLoggedIn from '../components/NotLoggedIn'; import SettingsHeader from '../components/SettingsHeader'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; -import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); @@ -25,10 +23,6 @@ class SettingsContainer extends Component { this.handleTabChange = this.handleTabChange.bind(this); } - componentWillMount () { - // this.props.fetchCommentsByUserId(this.props.userData.id); - } - handleTabChange(tab) { this.setState({ activeTab: tab @@ -36,18 +30,18 @@ class SettingsContainer extends Component { } render() { - const {loggedIn, userData, showSignInDialog, items, user} = this.props; + const {loggedIn, userData, showSignInDialog, user} = this.props; const {activeTab} = this.state; return ( }> - All Comments ({user.myComments.length}) - Profile Settings + {lang.t('allComments')} ({user.myComments.length}) + {lang.t('profileSettings')} - My comment History + {lang.t('myCommentHistory')} diff --git a/client/coral-settings/translations.json b/client/coral-settings/translations.json index f1d32b9d2..2a827a4f0 100644 --- a/client/coral-settings/translations.json +++ b/client/coral-settings/translations.json @@ -1,8 +1,14 @@ { "en":{ - "user-no-comment": "This user has not yet left a comment." + "userNoComment": "This user has not yet left a comment.", + "allComments": "All Comments", + "profileSettings": "Profile Settings", + "myCommentHistory": "My comment History" }, "es":{ - "user-no-comment": "Aún no ha escrito ningún comentario." + "userNoComment": "Aún no ha escrito ningún comentario.", + "allComments": "Todos los comentarios", + "profileSettings": "Configuración del perfil", + "myCommentHistory": "Mi historial de comentarios" } } From cebc5f2f4581c07f814f4a3294f25e763cfcdc03 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 07:09:11 -0300 Subject: [PATCH 07/28] Adding fragment views --- client/coral-embed-stream/src/Embed.js | 32 +++++++++++++++++++ .../src/graphql/mutations/index.js | 2 +- .../src/graphql/mutations/postComment.graphql | 2 ++ .../src/graphql/queries/index.js | 6 +++- .../src/graphql/queries/streamQuery.graphql | 2 ++ client/coral-framework/actions/user.js | 12 ------- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 87ab32fa3..bfb09857c 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -1,4 +1,5 @@ import React, {Component} from 'react'; +import gql from 'graphql-tag'; import {compose} from 'react-apollo'; import {connect} from 'react-redux'; import {isEqual} from 'lodash'; @@ -195,6 +196,37 @@ const mapDispatchToProps = dispatch => ({ dispatch: d => dispatch(d) }); +// import commentView from './graphql/fragments/commentView.graphql'; +// +// Embed.fragments = { +// entry: commentView +// }; + +Embed.fragments = { + entry: gql` + fragment commentView on Comment { + id + body + created_at + user { + id + name: displayName + settings { + bio + } + } + actions { + type: action_type + count + current: current_user { + id + created_at + } + } + } + `, +}; + export default compose( connect(mapStateToProps, mapDispatchToProps), postComment, diff --git a/client/coral-embed-stream/src/graphql/mutations/index.js b/client/coral-embed-stream/src/graphql/mutations/index.js index 5fbfbf9d2..b97e5ff22 100644 --- a/client/coral-embed-stream/src/graphql/mutations/index.js +++ b/client/coral-embed-stream/src/graphql/mutations/index.js @@ -7,7 +7,7 @@ import commentView from '../fragments/commentView.graphql'; export const postComment = graphql(POST_COMMENT, { options: () => ({ - fragments: [commentView] + fragments: commentView }), props: ({mutate}) => ({ postItem: ({asset_id, body, parent_id} /* , type */ ) => { diff --git a/client/coral-embed-stream/src/graphql/mutations/postComment.graphql b/client/coral-embed-stream/src/graphql/mutations/postComment.graphql index 46ef879ce..499871766 100644 --- a/client/coral-embed-stream/src/graphql/mutations/postComment.graphql +++ b/client/coral-embed-stream/src/graphql/mutations/postComment.graphql @@ -1,3 +1,5 @@ +#import "../fragments/commentView.graphql" + mutation CreateComment ($asset_id: ID!, $parent_id: ID, $body: String!) { createComment(asset_id:$asset_id, parent_id:$parent_id, body:$body) { ...commentView diff --git a/client/coral-embed-stream/src/graphql/queries/index.js b/client/coral-embed-stream/src/graphql/queries/index.js index ae01ea9f4..0bdc6cdaf 100644 --- a/client/coral-embed-stream/src/graphql/queries/index.js +++ b/client/coral-embed-stream/src/graphql/queries/index.js @@ -5,5 +5,9 @@ import pym from 'coral-framework/PymConnection'; let url = pym.parentUrl.split('#')[0] || 'http://localhost:3000/'; export const queryStream = graphql(STREAM_QUERY, { - options: {variables: {asset_url: url}} + options: () => ({ + variables: { + asset_url: url + } + }) }); diff --git a/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql b/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql index 5c5018d41..77f8ed356 100644 --- a/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql +++ b/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql @@ -1,3 +1,5 @@ +#import "../fragments/commentView.graphql" + query AssetQuery($asset_url: String!) { asset(url: $asset_url) { id diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js index a9bf0546a..4620262d3 100644 --- a/client/coral-framework/actions/user.js +++ b/client/coral-framework/actions/user.js @@ -1,5 +1,4 @@ import * as actions from '../constants/user'; -import * as assetActions from '../constants/assets'; import {addNotification} from '../actions/notification'; import coralApi from '../helpers/response'; @@ -20,14 +19,3 @@ 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 => { - // TODO -}; From c1d3cdce7738d3fe6feca8dbfc325342d0421254 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 08:11:52 -0300 Subject: [PATCH 08/28] deprecating items --- .../coral-framework/store/itemActions.js | 198 ------------------ 1 file changed, 198 deletions(-) delete mode 100644 test/client/coral-framework/store/itemActions.js diff --git a/test/client/coral-framework/store/itemActions.js b/test/client/coral-framework/store/itemActions.js deleted file mode 100644 index 24d06f928..000000000 --- a/test/client/coral-framework/store/itemActions.js +++ /dev/null @@ -1,198 +0,0 @@ -import 'react'; -import 'redux'; -import {expect} from 'chai'; -import fetchMock from 'fetch-mock'; -import * as actions from '../../../../client/coral-framework/actions/items'; -import {Map} from 'immutable'; - -import configureStore from 'redux-mock-store'; - -const mockStore = configureStore(); - -describe('itemActions', () => { - let store; - - beforeEach(() => { - store = mockStore(new Map({})); - fetchMock.restore(); - - }); - - describe('getStream', () => { - const assetUrl = 'http://www.test.com'; - const response = { - assets: [{ - id: '1234', url: assetUrl - }], - comments: [ - {body: 'stuff', id: '123'}, - {body: 'morestuff', id: '456'} - ], - actions: [ - { - action_type: 'like', - item_id: '123', - count: 1, - id: 'like_123', - current_user: false - }, - { - action_type: 'flag', - item_id: '456', - count: 5, - id: 'flag_456', - current_user: true - } - ] - }; - - it('should get an stream from an asset_url and send the appropriate dispatches', () => { - fetchMock.get('*', JSON.stringify(response)); - return actions.getStream(assetUrl)(store.dispatch) - .then((res) => { - expect(fetchMock.calls().matched[0][0]).to.equal('/api/v1/stream?asset_url=http%3A%2F%2Fwww.test.com'); - expect(res).to.deep.equal(response); - expect(store.getActions()[1]).to.deep.equal({ - type: actions.ADD_ITEM, - item: response.comments[0], - item_type: 'comments', - id: '123' - }); - expect(store.getActions()[2]).to.deep.equal({ - type: actions.ADD_ITEM, - item: response.comments[1], - item_type: 'comments', - id: '456' - }); - }); - }); - it('should handle an error', () => { - fetchMock.get('*', 404); - return actions.getStream(assetUrl)(store.dispatch) - .catch((err) => { - expect(err).to.be.truthy; - }); - }); - }); - - // Disabling tests for this function until is is used again. - xdescribe('getItemsArray', () => { - const response = {items: [{type: 'comment', id: '123'}, {type: 'comment', id: '456'}]}; - const ids = [1, 2]; - - it('should get an item from an array of ids and send the appropriate dispatches', () => { - fetchMock.get('*', JSON.stringify(response)); - return actions.getItemsArray(ids)(store.dispatch) - .then((res) => { - expect(res).to.deep.equal(response.items); - expect(store.getActions()[0]).to.deep.equal({ - type: actions.ADD_ITEM, - item: { - type: 'comment', - id: '123' - }, - id: '123' - }); - expect(store.getActions()[1]).to.deep.equal({ - type: actions.ADD_ITEM, - item: { - type: 'comment', id: '456' - }, - id: '456' - }); - }); - }); - it('should handle an error', () => { - fetchMock.get('*', 404); - return actions.getItemsArray(ids)(store.dispatch) - .catch((err) => { - expect(err).to.be.truthy; - }); - }); - }); - - // NEED TO FIGURE OUT HOW TO TEST WITH CSRF TOKEN IN. - xdescribe('postItem', () => { - const item = { - type: 'comments', - data: {body: 'stuff'} - }; - - it ('should post an item, return an id, then dispatch that item to the store', () => { - fetchMock.post('*', {id: '123'}); - return actions.postItem(item.data, item.type, undefined)(store.dispatch, store.getState) - .then((id) => { - expect(fetchMock.calls().matched[0][1]).to.deep.equal( - { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type':'application/json' - }, - credentials: 'same-origin', - body: JSON.stringify(item.data) - } - ); - expect(id).to.deep.equal({id: '123'}); - expect(store.getActions()[0]).to.deep.equal({ - type: actions.ADD_ITEM, - item: { - body: 'stuff', - id: '123' - }, - item_type: 'comments', - id: '123' - }); - }); - }); - it('should handle an error', () => { - fetchMock.post('*', 404); - return actions.postItem(item)(store.dispatch, store.getState) - .catch((err) => { - expect(err).to.be.truthy; - }); - }); - }); - - xdescribe('postAction', () => { - it ('should post an action', () => { - fetchMock.post('*', {id: '456'}); - const action = { - action_type: 'flag', - detail: 'Comment smells funny' - }; - return actions.postAction('abc', 'comments', action)(store.dispatch, store.getState) - .then(response => { - expect(fetchMock.calls().matched[0][0]).to.equal('/api/v1/comments/abc/actions'); - expect(response).to.deep.equal({id:'456'}); - }); - }); - - it('should handle an error', () => { - fetchMock.post('*', 404); - return actions.postAction('abc', 'flag', '123')(store.dispatch, store.getState) - .catch((err) => { - expect(err).to.be.truthy; - }); - }); - }); - - describe('deleteAction', () => { - it ('should remove an action', () => { - fetchMock.delete('*', {}); - return actions.deleteAction('abc', 'flag', '123', 'comments')(store.dispatch) - .then(response => { - expect(fetchMock.calls().matched[0][0]).to.equal('/api/v1/actions/abc'); - expect(response).to.deep.equal({}); - }); - }); - - xit('should handle an error', () => { - fetchMock.post('*', 404); - return actions.postAction('abc', 'flag', '123')(store.dispatch, store.getState) - .catch((err) => { - expect(err).to.be.truthy; - }); - }); - }); -}); From 14483a39a02bb4f06e57a71188bac2bd6c3a1c0e Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 08:38:03 -0300 Subject: [PATCH 09/28] Tests passing, removing legacy items --- .../coral-framework/store/itemReducer.js | 95 ------------------- 1 file changed, 95 deletions(-) delete mode 100644 test/client/coral-framework/store/itemReducer.js diff --git a/test/client/coral-framework/store/itemReducer.js b/test/client/coral-framework/store/itemReducer.js deleted file mode 100644 index 16377327b..000000000 --- a/test/client/coral-framework/store/itemReducer.js +++ /dev/null @@ -1,95 +0,0 @@ -import {Map, fromJS} from 'immutable'; -import {expect} from 'chai'; -import itemsReducer from '../../../../client/coral-framework/reducers/items'; - -describe ('itemsReducer', () => { - describe('ADD_ITEM', () => { - it('should add an item', () => { - const action = { - type: 'ADD_ITEM', - item: { - body: 'stuff', - id: '123' - }, - item_type: 'comments', - id: '123' - }; - const store = new Map({}); - const result = itemsReducer(store, action); - expect(result.getIn(['comments', '123']).toJS()).to.deep.equal({ - body: 'stuff', - id: '123' - }); - }); - }); - - describe ('UPDATE_ITEM', () => { - it ('should update an item', () => { - const action = { - type: 'UPDATE_ITEM', - property: 'stuff', - value: 'things', - item_type: 'comments', - id: '123' - }; - const store = fromJS({ - 'comments': { - '123': { - id: '123', - stuff: 'morestuff' - } - } - }); - const result = itemsReducer(store, action); - expect(result.getIn(['comments', '123']).toJS()).to.deep.equal({ - id: '123', - stuff: 'things' - }); - }); - }); - - describe('APPEND_ITEM_ARRAY', () => { - let action; - let store; - - beforeEach (() => { - action = { - type: 'APPEND_ITEM_ARRAY', - property: 'stuff', - value: 'things', - id: '123', - item_type: 'comments' - }; - store = fromJS({ - 'comments': { - '123': { - id: '123', - stuff: ['morestuff'] - } - } - }); - }); - it ('should append to an existing array', () => { - const result = itemsReducer(store, action); - expect(result.getIn(['comments', '123']).toJS()).to.deep.equal({ - id: '123', - stuff: ['morestuff', 'things'] - }); - }); - it ('should create a new array', () => { - store = fromJS({ - 'comments': { - '123': { - id: '123' - } - } - }); - const result = itemsReducer(store, action); - expect(result.getIn(['comments', '123']).toJS()).to.deep.equal({ - id: '123', - stuff: ['things'] - }); - }); - }); - -}); From 30b6f8d112c0c3afb9e5faf8188ae7091a4c3602 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 11:04:23 -0300 Subject: [PATCH 10/28] my Comment History working :tada: : TADA : --- .../containers/ConfigureStreamContainer.js | 6 ++-- client/coral-embed-stream/src/Embed.js | 36 ++----------------- client/coral-framework/client.js | 1 + .../graphql/fragments/commentView.graphql | 0 .../graphql/mutations/deleteAction.graphql | 0 .../graphql/mutations/index.js | 0 .../graphql/mutations/postAction.graphql | 0 .../graphql/mutations/postComment.graphql | 0 .../graphql/queries/index.js | 5 ++- .../graphql/queries/myCommentHistory.graphql | 9 +++++ .../graphql/queries/streamQuery.graphql | 0 client/coral-plugin-history/CommentHistory.js | 12 +++---- .../containers/SettingsContainer.js | 22 +++++++++--- 13 files changed, 42 insertions(+), 49 deletions(-) rename client/{coral-embed-stream/src => coral-framework}/graphql/fragments/commentView.graphql (100%) rename client/{coral-embed-stream/src => coral-framework}/graphql/mutations/deleteAction.graphql (100%) rename client/{coral-embed-stream/src => coral-framework}/graphql/mutations/index.js (100%) rename client/{coral-embed-stream/src => coral-framework}/graphql/mutations/postAction.graphql (100%) rename client/{coral-embed-stream/src => coral-framework}/graphql/mutations/postComment.graphql (100%) rename client/{coral-embed-stream/src => coral-framework}/graphql/queries/index.js (72%) create mode 100644 client/coral-framework/graphql/queries/myCommentHistory.graphql rename client/{coral-embed-stream/src => coral-framework}/graphql/queries/streamQuery.graphql (100%) diff --git a/client/coral-configure/containers/ConfigureStreamContainer.js b/client/coral-configure/containers/ConfigureStreamContainer.js index 90835ab3f..7b4b7c9af 100644 --- a/client/coral-configure/containers/ConfigureStreamContainer.js +++ b/client/coral-configure/containers/ConfigureStreamContainer.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; +import {compose} from 'react-apollo'; import {I18n} from '../../coral-framework'; import {updateOpenStatus, updateConfiguration} from '../../coral-framework/actions/asset'; @@ -89,7 +90,6 @@ const mapDispatchToProps = dispatch => ({ updateConfiguration: newConfig => dispatch(updateConfiguration(newConfig)) }); -export default connect( - mapStateToProps, - mapDispatchToProps +export default compose( + connect(mapStateToProps, mapDispatchToProps) )(ConfigureStreamContainer); diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index bfb09857c..c787b075f 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -1,5 +1,4 @@ import React, {Component} from 'react'; -import gql from 'graphql-tag'; import {compose} from 'react-apollo'; import {connect} from 'react-redux'; import {isEqual} from 'lodash'; @@ -10,8 +9,8 @@ const {logout, showSignInDialog} = authActions; const {addNotification, clearNotification} = notificationActions; const {fetchAssetSuccess} = assetActions; -import {queryStream} from './graphql/queries'; -import {postComment, postAction, deleteAction} from './graphql/mutations'; +import {queryStream} from 'coral-framework/graphql/queries'; +import {postComment, postAction, deleteAction} from 'coral-framework/graphql/mutations'; import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework'; import Stream from './Stream'; @@ -196,37 +195,6 @@ const mapDispatchToProps = dispatch => ({ dispatch: d => dispatch(d) }); -// import commentView from './graphql/fragments/commentView.graphql'; -// -// Embed.fragments = { -// entry: commentView -// }; - -Embed.fragments = { - entry: gql` - fragment commentView on Comment { - id - body - created_at - user { - id - name: displayName - settings { - bio - } - } - actions { - type: action_type - count - current: current_user { - id - created_at - } - } - } - `, -}; - export default compose( connect(mapStateToProps, mapDispatchToProps), postComment, diff --git a/client/coral-framework/client.js b/client/coral-framework/client.js index 40a539634..b4a7a38df 100644 --- a/client/coral-framework/client.js +++ b/client/coral-framework/client.js @@ -2,6 +2,7 @@ import ApolloClient, {addTypename} from 'apollo-client'; import getNetworkInterface from './transport'; export const client = new ApolloClient({ + connectToDevTools: true, queryTransformer: addTypename, dataIdFromObject: (result) => { if (result.id && result.__typename) { // eslint-disable-line no-underscore-dangle diff --git a/client/coral-embed-stream/src/graphql/fragments/commentView.graphql b/client/coral-framework/graphql/fragments/commentView.graphql similarity index 100% rename from client/coral-embed-stream/src/graphql/fragments/commentView.graphql rename to client/coral-framework/graphql/fragments/commentView.graphql diff --git a/client/coral-embed-stream/src/graphql/mutations/deleteAction.graphql b/client/coral-framework/graphql/mutations/deleteAction.graphql similarity index 100% rename from client/coral-embed-stream/src/graphql/mutations/deleteAction.graphql rename to client/coral-framework/graphql/mutations/deleteAction.graphql diff --git a/client/coral-embed-stream/src/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js similarity index 100% rename from client/coral-embed-stream/src/graphql/mutations/index.js rename to client/coral-framework/graphql/mutations/index.js diff --git a/client/coral-embed-stream/src/graphql/mutations/postAction.graphql b/client/coral-framework/graphql/mutations/postAction.graphql similarity index 100% rename from client/coral-embed-stream/src/graphql/mutations/postAction.graphql rename to client/coral-framework/graphql/mutations/postAction.graphql diff --git a/client/coral-embed-stream/src/graphql/mutations/postComment.graphql b/client/coral-framework/graphql/mutations/postComment.graphql similarity index 100% rename from client/coral-embed-stream/src/graphql/mutations/postComment.graphql rename to client/coral-framework/graphql/mutations/postComment.graphql diff --git a/client/coral-embed-stream/src/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js similarity index 72% rename from client/coral-embed-stream/src/graphql/queries/index.js rename to client/coral-framework/graphql/queries/index.js index 0bdc6cdaf..727102df9 100644 --- a/client/coral-embed-stream/src/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -1,7 +1,8 @@ import {graphql} from 'react-apollo'; import STREAM_QUERY from './streamQuery.graphql'; -import pym from 'coral-framework/PymConnection'; +import MY_COMMENT_HISTORY from './myCommentHistory.graphql'; +import pym from 'coral-framework/PymConnection'; let url = pym.parentUrl.split('#')[0] || 'http://localhost:3000/'; export const queryStream = graphql(STREAM_QUERY, { @@ -11,3 +12,5 @@ export const queryStream = graphql(STREAM_QUERY, { } }) }); + +export const myCommentHistory = graphql(MY_COMMENT_HISTORY, {}); diff --git a/client/coral-framework/graphql/queries/myCommentHistory.graphql b/client/coral-framework/graphql/queries/myCommentHistory.graphql new file mode 100644 index 000000000..6a4309dac --- /dev/null +++ b/client/coral-framework/graphql/queries/myCommentHistory.graphql @@ -0,0 +1,9 @@ +query myCommentHistory { + me { + comments { + id + body + created_at + } + } +} diff --git a/client/coral-embed-stream/src/graphql/queries/streamQuery.graphql b/client/coral-framework/graphql/queries/streamQuery.graphql similarity index 100% rename from client/coral-embed-stream/src/graphql/queries/streamQuery.graphql rename to client/coral-framework/graphql/queries/streamQuery.graphql diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index add7c121a..dc505e38a 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -7,12 +7,12 @@ const CommentHistory = props => {
{props.comments.map((comment, i) => { - const asset = props.assets.find(asset => asset.id === comment.asset_id); - return ; + return
{comment.body}
+ {/*return ;*/} })}
diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 6dc2bf3b9..752f01c40 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -1,12 +1,15 @@ import {connect} from 'react-redux'; +import {compose} from 'react-apollo'; import React, {Component} from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; +import {myCommentHistory} from 'coral-framework/graphql/queries'; import {saveBio} from 'coral-framework/actions/user'; import BioContainer from './BioContainer'; import {TabBar, Tab, TabContent} from 'coral-ui'; import NotLoggedIn from '../components/NotLoggedIn'; +import CommentHistory from 'coral-plugin-history/CommentHistory'; import SettingsHeader from '../components/SettingsHeader'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; @@ -30,9 +33,13 @@ class SettingsContainer extends Component { } render() { - const {loggedIn, userData, showSignInDialog, user} = this.props; + const {loggedIn, userData, showSignInDialog, user, data} = this.props; const {activeTab} = this.state; + if (data.loading) { + return
Loading
+ } + return ( }> @@ -41,7 +48,12 @@ class SettingsContainer extends Component { {lang.t('profileSettings')} - {lang.t('myCommentHistory')} + { + data.me.comments.length ? + + : +

{lang.t('user-no-comment')}

+ }
@@ -59,7 +71,7 @@ const mapDispatchToProps = dispatch => ({ saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)) }); -export default connect( - mapStateToProps, - mapDispatchToProps +export default compose( + connect(mapStateToProps, mapDispatchToProps), + myCommentHistory )(SettingsContainer); From d19d2ebe4895b72ede116e6f9653a268330bc18a Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 11:19:34 -0300 Subject: [PATCH 11/28] My Comment History Working - linted --- client/coral-plugin-history/CommentHistory.js | 15 +++++++-------- .../containers/SettingsContainer.js | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/client/coral-plugin-history/CommentHistory.js b/client/coral-plugin-history/CommentHistory.js index dc505e38a..1940f2695 100644 --- a/client/coral-plugin-history/CommentHistory.js +++ b/client/coral-plugin-history/CommentHistory.js @@ -7,12 +7,11 @@ const CommentHistory = props => {
{props.comments.map((comment, i) => { - return
{comment.body}
- {/*return ;*/} + return ; })}
@@ -20,8 +19,8 @@ const CommentHistory = props => { }; CommentHistory.propTypes = { - comments: PropTypes.arrayOf(PropTypes.object).isRequired, - assets: PropTypes.arrayOf(PropTypes.object).isRequired + comments: PropTypes.array.isRequired, + asset: PropTypes.object.isRequired }; export default CommentHistory; diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index 752f01c40..ff8b76628 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -6,8 +6,10 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import {myCommentHistory} from 'coral-framework/graphql/queries'; import {saveBio} from 'coral-framework/actions/user'; +import {link} from 'coral-framework/PymConnection'; + import BioContainer from './BioContainer'; -import {TabBar, Tab, TabContent} from 'coral-ui'; +import {TabBar, Tab, TabContent, Spinner} from 'coral-ui'; import NotLoggedIn from '../components/NotLoggedIn'; import CommentHistory from 'coral-plugin-history/CommentHistory'; import SettingsHeader from '../components/SettingsHeader'; @@ -33,13 +35,15 @@ class SettingsContainer extends Component { } render() { - const {loggedIn, userData, showSignInDialog, user, data} = this.props; + const {loggedIn, userData, asset, showSignInDialog, user, data} = this.props; const {activeTab} = this.state; if (data.loading) { - return
Loading
+ return ; } + console.log(this.props); + return ( }> @@ -50,7 +54,11 @@ class SettingsContainer extends Component { { data.me.comments.length ? - + :

{lang.t('user-no-comment')}

} @@ -64,7 +72,8 @@ class SettingsContainer extends Component { } const mapStateToProps = state => ({ - user: state.user.toJS() + user: state.user.toJS(), + asset: state.asset.toJS() }); const mapDispatchToProps = dispatch => ({ From b3eb3e3c43044890e3a1f4ad9e7ffe4c30fb905e Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 11:44:51 -0300 Subject: [PATCH 12/28] Updated tests for Comment History --- client/coral-plugin-history/Comment.js | 1 + .../CommentHistory.spec.js | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js index df61b9e7d..1235f1d72 100644 --- a/client/coral-plugin-history/Comment.js +++ b/client/coral-plugin-history/Comment.js @@ -2,6 +2,7 @@ import React, {PropTypes} from 'react'; import styles from './Comment.css'; const Comment = props => { + console.log(props.asset) return (

diff --git a/test/client/coral-plugin-history/CommentHistory.spec.js b/test/client/coral-plugin-history/CommentHistory.spec.js index 9d81f79cd..945bf45a3 100644 --- a/test/client/coral-plugin-history/CommentHistory.spec.js +++ b/test/client/coral-plugin-history/CommentHistory.spec.js @@ -6,14 +6,26 @@ 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 asset = { + '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 + }; beforeEach(() => { - render = shallow({}}/>); + render = shallow({}}/>); }); it('should render Comments as children when given comments and assets', () => { - const wrapper = mount({}}/>); + const wrapper = mount({}}/>); expect(wrapper.find('.commentHistory__list').children()).to.have.length(7); }); From 7c132600f07a8d8d34542e787b67250fa026d1e9 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 25 Jan 2017 11:47:35 -0300 Subject: [PATCH 13/28] Console log and typo --- client/coral-plugin-history/Comment.js | 1 - client/coral-settings/containers/SettingsContainer.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js index 1235f1d72..df61b9e7d 100644 --- a/client/coral-plugin-history/Comment.js +++ b/client/coral-plugin-history/Comment.js @@ -2,7 +2,6 @@ import React, {PropTypes} from 'react'; import styles from './Comment.css'; const Comment = props => { - console.log(props.asset) return (

diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/SettingsContainer.js index ff8b76628..779af043a 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/SettingsContainer.js @@ -60,7 +60,7 @@ class SettingsContainer extends Component { link={link} /> : -

{lang.t('user-no-comment')}

+

{lang.t('userNoComment')}

} From ad9684acd9d4a7a6f95266b07e945aa543fbbb2e Mon Sep 17 00:00:00 2001 From: David Erwin Date: Wed, 25 Jan 2017 14:08:19 -0500 Subject: [PATCH 14/28] Create load stream by id route --- .../src/graphql/queries/index.js | 15 ++++++-- routes/assets/index.js | 31 +++++++++++++++++ routes/index.js | 8 +---- views/article.ejs | 34 +++++++++++++------ 4 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 routes/assets/index.js diff --git a/client/coral-embed-stream/src/graphql/queries/index.js b/client/coral-embed-stream/src/graphql/queries/index.js index ae01ea9f4..221d78c4d 100644 --- a/client/coral-embed-stream/src/graphql/queries/index.js +++ b/client/coral-embed-stream/src/graphql/queries/index.js @@ -1,9 +1,18 @@ import {graphql} from 'react-apollo'; import STREAM_QUERY from './streamQuery.graphql'; -import pym from 'coral-framework/PymConnection'; -let url = pym.parentUrl.split('#')[0] || 'http://localhost:3000/'; +function getQueryVariable(variable) { + let query = window.location.search.substring(1); + let vars = query.split('&'); + for (let i = 0; i < vars.length; i++) { + let pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) === variable) { + return decodeURIComponent(pair[1]); + } + } + console.log('Query variable %s not found', variable); +} export const queryStream = graphql(STREAM_QUERY, { - options: {variables: {asset_url: url}} + options: {variables: {asset_url: getQueryVariable('asset_url')}} }); diff --git a/routes/assets/index.js b/routes/assets/index.js new file mode 100644 index 000000000..46ad378e5 --- /dev/null +++ b/routes/assets/index.js @@ -0,0 +1,31 @@ +const express = require('express'); +const router = express.Router(); + +const Assets = require('../../services/assets'); + +const body = 'Lorem ipsum dolor sponge amet, consectetur adipiscing clam. Ut lobortis sollicitudin pillar a ornare. Curabitur dignissim vestibulum cay non rhoncus. Cras laoreet ante vel nunc hendrerit, shelf imperdiet neque egestas. Suspendisse aliquet iaculis fermentum. Talk volutpat, tellus posuere laoreet consequat, mi lacus laoreet massa, sed vehicula mauris velit non lectus. Integer non trust nec neque congue faucibus porttitor sit amet elkhorn.'; + +router.get('/id/:asset_id', (req, res, next) => { + + return Assets.findById(req.params.asset_id) + .then(asset => { + res.render('article', { + title: asset.title, + asset_url: asset.url, + body: '', + basePath: '/client/embed/stream' + }); + }) + .catch((err) => next(err)); +}); + +router.get('/title/:asset_title', (req, res) => { + return res.render('article', { + title: req.params.asset_title.split('-').join(' '), + asset_url: '', + body: body, + basePath: '/client/embed/stream' + }); +}); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js index 9ab0dff14..6684f5676 100644 --- a/routes/index.js +++ b/routes/index.js @@ -4,6 +4,7 @@ const router = express.Router(); router.use('/api/v1', require('./api')); router.use('/admin', require('./admin')); router.use('/embed', require('./embed')); +router.use('/assets', require('./assets')); router.get('/', (req, res) => { return res.render('article', { @@ -12,11 +13,4 @@ router.get('/', (req, res) => { }); }); -router.get('/assets/:asset_title', (req, res) => { - return res.render('article', { - title: req.params.asset_title.split('-').join(' '), - basePath: '/client/embed/stream' - }); -}); - module.exports = router; diff --git a/views/article.ejs b/views/article.ejs index c63acc62b..f5f56420e 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -20,15 +20,7 @@

<%= title %>

-

- Lorem ipsum dolor sponge amet, consectetur adipiscing clam. - Ut lobortis sollicitudin pillar a ornare. Curabitur dignissim - vestibulum cay non rhoncus. Cras laoreet ante vel nunc hendrerit, - shelf imperdiet neque egestas. Suspendisse aliquet iaculis fermentum. - Talk volutpat, tellus posuere laoreet consequat, mi lacus laoreet massa, - sed vehicula mauris velit non lectus. Integer non trust nec neque congue - faucibus porttitor sit amet elkhorn. -

+

<%= body %>

Visit the moderation console

@@ -36,13 +28,33 @@