From a5f6e1f343061cd0246052a479ed9f046c419253 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 11:32:46 -0300 Subject: [PATCH 01/19] Adding coral-reaction --- client/coral-reaction/actions.js | 0 .../components/CoralReaction.js | 98 +++++++++ client/coral-reaction/components/style.css | 30 +++ client/coral-reaction/constants.js | 0 .../containers/CoralReaction.js | 188 ++++++++++++++++++ client/coral-reaction/helpers.js | 4 + client/coral-reaction/index.js | 3 + client/coral-reaction/reducer.js | 0 client/coral-reaction/typeDefGenerator.js | 80 ++++++++ 9 files changed, 403 insertions(+) create mode 100644 client/coral-reaction/actions.js create mode 100644 client/coral-reaction/components/CoralReaction.js create mode 100644 client/coral-reaction/components/style.css create mode 100644 client/coral-reaction/constants.js create mode 100644 client/coral-reaction/containers/CoralReaction.js create mode 100644 client/coral-reaction/helpers.js create mode 100644 client/coral-reaction/index.js create mode 100644 client/coral-reaction/reducer.js create mode 100644 client/coral-reaction/typeDefGenerator.js diff --git a/client/coral-reaction/actions.js b/client/coral-reaction/actions.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/coral-reaction/components/CoralReaction.js b/client/coral-reaction/components/CoralReaction.js new file mode 100644 index 000000000..f691e2bb7 --- /dev/null +++ b/client/coral-reaction/components/CoralReaction.js @@ -0,0 +1,98 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import cn from 'classnames'; +import styles from './style.css'; + +import {capitalize} from '../helpers'; +import {getMyActionSummary, getTotalActionCount} from 'coral-framework/utils'; + +class CoralReaction extends React.Component { + handleClick = () => { + const name = this.props.children; + const {postReaction, deleteReaction, showSignInDialog} = this.props; + + const {root: {me}, comment} = this.props; + + const myActionSummary = getMyActionSummary( + `${capitalize(name)}ActionSummary`, + comment + ); + + // If the current user does not exist, trigger sign in dialog. + if (!me) { + showSignInDialog(); + return; + } + + // If the current user is banned, do nothing. + if (me.status === 'BANNED') { + return; + } + + if (myActionSummary) { + deleteReaction(myActionSummary.current_user.id, comment.id); + } else { + postReaction({ + item_id: comment.id, + item_type: 'COMMENTS' + }); + } + }; + + render() { + const name = this.props.children; + const {comment} = this.props; + + console.log(this.props) + + if (!comment) { + return null; + } + + const myReaction = getMyActionSummary(`${capitalize(name)}ActionSummary`, comment); + let count = getTotalActionCount(`${capitalize(name)}ActionSummary`, comment); + + return ( +
+ +
+ ); + } +} + +CoralReaction.propTypes = { + children: PropTypes.string.isRequired, + icon: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + tag: PropTypes.string, + translations: PropTypes.object +}; + +export default CoralReaction; + +/** + * + * icon: Could be a string or a component https://material.io/icons/ + **/ diff --git a/client/coral-reaction/components/style.css b/client/coral-reaction/components/style.css new file mode 100644 index 000000000..a82f20ff6 --- /dev/null +++ b/client/coral-reaction/components/style.css @@ -0,0 +1,30 @@ +.like { + display: inline-block; +} + +.button { + color: #2a2a2a; + margin: 5px 10px 5px 0px; + background: none; + padding: 0px; + border: none; + font-size: inherit; + + &:hover { + color: #767676; + cursor: pointer; + } + + &.liked { + color: rgb(0,134,227); + + &:hover { + color: rgb(0,134,227); + cursor: pointer; + } + } +} + +.icon { + padding: 0 5px; +} diff --git a/client/coral-reaction/constants.js b/client/coral-reaction/constants.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/coral-reaction/containers/CoralReaction.js b/client/coral-reaction/containers/CoralReaction.js new file mode 100644 index 000000000..f8880a935 --- /dev/null +++ b/client/coral-reaction/containers/CoralReaction.js @@ -0,0 +1,188 @@ +import get from 'lodash/get'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {compose, gql, graphql} from 'react-apollo'; +import CoralReaction from '../components/CoralReaction'; +import withFragments from 'coral-framework/hocs/withFragments'; +import {showSignInDialog} from 'coral-framework/actions/auth'; +import {capitalize} from '../helpers'; + +const name = 'love'; + +const isReaction = a => a.__typename === `${capitalize(name)}ActionSummary`; + +const COMMENT_FRAGMENT = gql` + fragment ${capitalize(name)}Button_updateFragment on Comment { + action_summaries { + ... on ${capitalize(name)}ActionSummary { + count + current_user { + id + } + } + } + } +`; + +const withDeleteReaction = graphql( + gql` + mutation deleteReaction($id: ID!) { + deleteAction(id:$id) { + errors { + translation_key + } + } + } + `, + { + props: ({mutate}) => ({ + deleteReaction: (id, commentId) => { + return mutate({ + variables: {id}, + optimisticResponse: { + deleteAction: { + __typename: 'DeleteActionResponse', + errors: null + } + }, + update: proxy => { + const fragmentId = `Comment_${commentId}`; + + // Read the data from our cache for this query. + const data = proxy.readFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId + }); + + // Check whether we liked this comment. + const idx = data.action_summaries.findIndex(isReaction); + if ( + idx < 0 || + get(data.action_summaries[idx], 'current_user.id') !== id + ) { + return; + } + + data.action_summaries[idx] = { + ...data.action_summaries[idx], + count: data.action_summaries[idx].count - 1, + current_user: null + }; + + // Write our data back to the cache. + proxy.writeFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId, + data + }); + } + }); + } + }) + } +); + +const withPostReaction = graphql( + gql` + mutation create${capitalize(name)}($${name}: Create${capitalize(name)}Input!) { + create${capitalize(name)}(${name}: $${capitalize(name)}) { + ${name} { + id + } + errors { + translation_key + } + } + } + `, + { + props: ({mutate}) => ({ + postReaction: reaction => { + return mutate({ + variables: {reaction}, + optimisticResponse: { + [`create${capitalize(name)}`]: { + __typename: `Create${capitalize(name)}Response`, + errors: null, + [name]: { + __typename: `${capitalize(name)}Action`, + id: 'pending' + } + } + }, + update: (proxy, mutationResult) => { + const fragmentId = `Comment_${reaction.item_id}`; + + // Read the data from our cache for this query. + const data = proxy.readFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId + }); + + // Add our comment from the mutation to the end. + let idx = data.action_summaries.findIndex(isReaction); + + // Check whether we already reactioned this comment. + if (idx >= 0 && data.action_summaries[idx].current_user) { + return; + } + + if (idx < 0) { + // Add initial action when it doesn't exist. + data.action_summaries.push({ + __typename: `${capitalize(name)}ActionSummary`, + count: 0, + current_user: null + }); + idx = data.action_summaries.length - 1; + } + + data.action_summaries[idx] = { + ...data.action_summaries[idx], + count: data.action_summaries[idx].count + 1, + current_user: mutationResult.data[`create${capitalize(name)}`][name] + }; + + // Write our data back to the cache. + proxy.writeFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId, + data + }); + } + }); + } + }) + } +); + +const mapDispatchToProps = dispatch => + bindActionCreators({showSignInDialog}, dispatch); + +const enhance = compose( + withFragments({ + root: gql` + fragment ${capitalize(name)}Button_root on RootQuery { + me { + status + } + } + `, + comment: gql` + fragment ${capitalize(name)}Button_comment on Comment { + action_summaries { + ... on ${capitalize(name)}ActionSummary { + count + current_user { + id + } + } + } + }` + }), + connect(null, mapDispatchToProps), + withDeleteReaction, + withPostReaction +); + +export default enhance(CoralReaction); diff --git a/client/coral-reaction/helpers.js b/client/coral-reaction/helpers.js new file mode 100644 index 000000000..0a267da59 --- /dev/null +++ b/client/coral-reaction/helpers.js @@ -0,0 +1,4 @@ +export function capitalize(str) { + const newString = new String(str); + return newString.charAt(0).toUpperCase() + newString.slice(1); +} diff --git a/client/coral-reaction/index.js b/client/coral-reaction/index.js new file mode 100644 index 000000000..d8527b9fd --- /dev/null +++ b/client/coral-reaction/index.js @@ -0,0 +1,3 @@ +import CoralReaction from './containers/CoralReaction'; + +export default CoralReaction; \ No newline at end of file diff --git a/client/coral-reaction/reducer.js b/client/coral-reaction/reducer.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/coral-reaction/typeDefGenerator.js b/client/coral-reaction/typeDefGenerator.js new file mode 100644 index 000000000..63c20684e --- /dev/null +++ b/client/coral-reaction/typeDefGenerator.js @@ -0,0 +1,80 @@ +export function typeDefGenerator(type = '') { + function capitalize(str) { + const newString = String.new(str); + return newString.charAt(0).toUpperCase() + newString.slice(1); + } + + return ` +enum ACTION_TYPE { + + # Represents a Like. + LIKE +} + +enum ASSET_METRICS_SORT { + + # Represents a LikeAction. + LIKE +} + +input CreateLikeInput { + + # The item's id for which we are to create a like. + item_id: ID! + + # The type of the item for which we are to create the like. + item_type: ACTION_ITEM_TYPE! +} + +# LikeAction is used by users who "like" a specific entity. +type LikeAction implements Action { + + # The ID of the action. + id: ID! + + # The author of the action. + user: User + + # The time when the Action was updated. + updated_at: Date + + # The time when the Action was created. + created_at: Date +} + +type LikeActionSummary implements ActionSummary { + + # The count of actions with this group. + count: Int + + # The current user's action. + current_user: LikeAction +} + +# A summary of counts related to all the Likes on an Asset. +type LikeAssetActionSummary implements AssetActionSummary { + + # Number of likes associated with actionable types on this this Asset. + actionCount: Int + + # Number of unique actionable types that are referenced by the likes. + actionableItemCount: Int +} + +type CreateLikeResponse implements Response { + + # The like that was created. + like: LikeAction + + # An array of errors relating to the mutation that occurred. + errors: [UserError] +} + +type RootMutation { + + # Creates a like on an entity. + createLike(like: CreateLikeInput!): CreateLikeResponse +} + +`; +} \ No newline at end of file From 011e5da26f9a0b8d82d17628f112cda8e67ea7f3 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 15:44:11 -0300 Subject: [PATCH 02/19] Almost fully working --- client/coral-framework/helpers/plugins.js | 2 +- .../helpers/strings.js} | 0 client/coral-framework/hocs/withReaction.js | 220 ++++++++++++++++++ client/coral-reaction/actions.js | 0 .../components/CoralReaction.js | 98 -------- client/coral-reaction/components/style.css | 30 --- client/coral-reaction/constants.js | 0 .../containers/CoralReaction.js | 188 --------------- client/coral-reaction/index.js | 3 - client/coral-reaction/reducer.js | 0 client/coral-reaction/typeDefGenerator.js | 80 ------- 11 files changed, 221 insertions(+), 400 deletions(-) rename client/{coral-reaction/helpers.js => coral-framework/helpers/strings.js} (100%) create mode 100644 client/coral-framework/hocs/withReaction.js delete mode 100644 client/coral-reaction/actions.js delete mode 100644 client/coral-reaction/components/CoralReaction.js delete mode 100644 client/coral-reaction/components/style.css delete mode 100644 client/coral-reaction/constants.js delete mode 100644 client/coral-reaction/containers/CoralReaction.js delete mode 100644 client/coral-reaction/index.js delete mode 100644 client/coral-reaction/reducer.js delete mode 100644 client/coral-reaction/typeDefGenerator.js diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index 20aee7abb..d701b933f 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -21,7 +21,7 @@ export function getSlotElements(slot, props = {}) { .filter(o => o.module.slots[slot]) .map(o => o.module.slots[slot])); return components - .map((component, i) => React.createElement(component, {...props, key: i})); + .map((component, i) => React.createElement(component, {key: i, ...props})); } function getComponentFragments(components) { diff --git a/client/coral-reaction/helpers.js b/client/coral-framework/helpers/strings.js similarity index 100% rename from client/coral-reaction/helpers.js rename to client/coral-framework/helpers/strings.js diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js new file mode 100644 index 000000000..b838941ad --- /dev/null +++ b/client/coral-framework/hocs/withReaction.js @@ -0,0 +1,220 @@ +import React from 'react'; +import get from 'lodash/get'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {compose, gql, graphql} from 'react-apollo'; +import withFragments from 'coral-framework/hocs/withFragments'; +import {showSignInDialog} from 'coral-framework/actions/auth'; +import {capitalize} from 'coral-framework/helpers/strings'; +import {getMyActionSummary, getTotalActionCount} from 'coral-framework/utils'; + +export default reaction => WrappedComponent => { + if (typeof reaction !== 'string') { + console.error('Reaction must be a valid string'); + return null; + } + + reaction = reaction.toLowerCase(); + + class WithReactions extends React.Component { + render() { + const {comment} = this.props; + + const reactionSummary = getMyActionSummary( + `${capitalize(reaction)}ActionSummary`, + comment + ); + const count = getTotalActionCount( + `${capitalize(reaction)}ActionSummary`, + comment + ); + + const withReactionProps = {reactionSummary, count}; + + return ; + } + } + + const isReaction = a => + a.__typereaction === `${capitalize(reaction)}ActionSummary`; + + const COMMENT_FRAGMENT = gql` + fragment ${capitalize(reaction)}Button_updateFragment on Comment { + action_summaries { + ... on ${capitalize(reaction)}ActionSummary { + count + current_user { + id + } + } + } + } + `; + + const withDeleteReaction = graphql( + gql` + mutation deleteReaction($id: ID!) { + deleteAction(id:$id) { + errors { + translation_key + } + } + } + `, + { + props: ({mutate}) => ({ + deleteReaction: (id, commentId) => { + return mutate({ + variables: {id}, + optimisticResponse: { + deleteAction: { + __typereaction: 'DeleteActionResponse', + errors: null + } + }, + update: proxy => { + const fragmentId = `Comment_${commentId}`; + + // Read the data from our cache for this query. + const data = proxy.readFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId + }); + + // Check whether we liked this comment. + const idx = data.action_summaries.findIndex(isReaction); + if ( + idx < 0 || + get(data.action_summaries[idx], 'current_user.id') !== id + ) { + return; + } + + data.action_summaries[idx] = { + ...data.action_summaries[idx], + count: data.action_summaries[idx].count - 1, + current_user: null + }; + + // Write our data back to the cache. + proxy.writeFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId, + data + }); + } + }); + } + }) + } + ); + + const withPostReaction = graphql( + gql` + mutation create${capitalize(reaction)}($${reaction}: Create${capitalize(reaction)}Input!) { + create${capitalize(reaction)}(${reaction}: $${reaction}) { + ${reaction} { + id + } + errors { + translation_key + } + } + } + `, + { + props: ({mutate}) => ({ + postReaction: reactionData => { + return mutate({ + variables: {[reaction]: reactionData}, + optimisticResponse: { + [`create${capitalize(reaction)}`]: { + __typereaction: `Create${capitalize(reaction)}Response`, + errors: null, + [reaction]: { + __typereaction: `${capitalize(reaction)}Action`, + id: 'pending' + } + } + }, + update: (proxy, mutationResult) => { + console.log(isReaction); + const fragmentId = `Comment_${reactionData.item_id}`; + + // Read the data from our cache for this query. + const data = proxy.readFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId + }); + + // Add our comment from the mutation to the end. + let idx = data.action_summaries.findIndex(isReaction); + console.log(data.action_summaries); + + // Check whether we already reactioned this comment. + if (idx >= 0 && data.action_summaries[idx].current_user) { + return; + } + + if (idx < 0) { + // Add initial action when it doesn't exist. + data.action_summaries.push({ + __typereaction: `${capitalize(reaction)}ActionSummary`, + count: 0, + current_user: null + }); + idx = data.action_summaries.length - 1; + } + + data.action_summaries[idx] = { + ...data.action_summaries[idx], + count: data.action_summaries[idx].count + 1, + current_user: mutationResult.data[ + `create${capitalize(reaction)}` + ][reaction] + }; + + // Write our data back to the cache. + proxy.writeFragment({ + fragment: COMMENT_FRAGMENT, + id: fragmentId, + data + }); + } + }); + } + }) + } + ); + + const mapDispatchToProps = dispatch => + bindActionCreators({showSignInDialog}, dispatch); + + const enhance = compose( + withFragments({ + root: gql` + fragment ${capitalize(reaction)}Button_root on RootQuery { + me { + status + } + } + `, + comment: gql` + fragment ${capitalize(reaction)}Button_comment on Comment { + action_summaries { + ... on ${capitalize(reaction)}ActionSummary { + count + current_user { + id + } + } + } + }` + }), + connect(null, mapDispatchToProps), + withDeleteReaction, + withPostReaction + ); + + return enhance(WithReactions); +}; diff --git a/client/coral-reaction/actions.js b/client/coral-reaction/actions.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/coral-reaction/components/CoralReaction.js b/client/coral-reaction/components/CoralReaction.js deleted file mode 100644 index f691e2bb7..000000000 --- a/client/coral-reaction/components/CoralReaction.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import cn from 'classnames'; -import styles from './style.css'; - -import {capitalize} from '../helpers'; -import {getMyActionSummary, getTotalActionCount} from 'coral-framework/utils'; - -class CoralReaction extends React.Component { - handleClick = () => { - const name = this.props.children; - const {postReaction, deleteReaction, showSignInDialog} = this.props; - - const {root: {me}, comment} = this.props; - - const myActionSummary = getMyActionSummary( - `${capitalize(name)}ActionSummary`, - comment - ); - - // If the current user does not exist, trigger sign in dialog. - if (!me) { - showSignInDialog(); - return; - } - - // If the current user is banned, do nothing. - if (me.status === 'BANNED') { - return; - } - - if (myActionSummary) { - deleteReaction(myActionSummary.current_user.id, comment.id); - } else { - postReaction({ - item_id: comment.id, - item_type: 'COMMENTS' - }); - } - }; - - render() { - const name = this.props.children; - const {comment} = this.props; - - console.log(this.props) - - if (!comment) { - return null; - } - - const myReaction = getMyActionSummary(`${capitalize(name)}ActionSummary`, comment); - let count = getTotalActionCount(`${capitalize(name)}ActionSummary`, comment); - - return ( -
- -
- ); - } -} - -CoralReaction.propTypes = { - children: PropTypes.string.isRequired, - icon: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), - tag: PropTypes.string, - translations: PropTypes.object -}; - -export default CoralReaction; - -/** - * - * icon: Could be a string or a component https://material.io/icons/ - **/ diff --git a/client/coral-reaction/components/style.css b/client/coral-reaction/components/style.css deleted file mode 100644 index a82f20ff6..000000000 --- a/client/coral-reaction/components/style.css +++ /dev/null @@ -1,30 +0,0 @@ -.like { - display: inline-block; -} - -.button { - color: #2a2a2a; - margin: 5px 10px 5px 0px; - background: none; - padding: 0px; - border: none; - font-size: inherit; - - &:hover { - color: #767676; - cursor: pointer; - } - - &.liked { - color: rgb(0,134,227); - - &:hover { - color: rgb(0,134,227); - cursor: pointer; - } - } -} - -.icon { - padding: 0 5px; -} diff --git a/client/coral-reaction/constants.js b/client/coral-reaction/constants.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/coral-reaction/containers/CoralReaction.js b/client/coral-reaction/containers/CoralReaction.js deleted file mode 100644 index f8880a935..000000000 --- a/client/coral-reaction/containers/CoralReaction.js +++ /dev/null @@ -1,188 +0,0 @@ -import get from 'lodash/get'; -import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; -import {compose, gql, graphql} from 'react-apollo'; -import CoralReaction from '../components/CoralReaction'; -import withFragments from 'coral-framework/hocs/withFragments'; -import {showSignInDialog} from 'coral-framework/actions/auth'; -import {capitalize} from '../helpers'; - -const name = 'love'; - -const isReaction = a => a.__typename === `${capitalize(name)}ActionSummary`; - -const COMMENT_FRAGMENT = gql` - fragment ${capitalize(name)}Button_updateFragment on Comment { - action_summaries { - ... on ${capitalize(name)}ActionSummary { - count - current_user { - id - } - } - } - } -`; - -const withDeleteReaction = graphql( - gql` - mutation deleteReaction($id: ID!) { - deleteAction(id:$id) { - errors { - translation_key - } - } - } - `, - { - props: ({mutate}) => ({ - deleteReaction: (id, commentId) => { - return mutate({ - variables: {id}, - optimisticResponse: { - deleteAction: { - __typename: 'DeleteActionResponse', - errors: null - } - }, - update: proxy => { - const fragmentId = `Comment_${commentId}`; - - // Read the data from our cache for this query. - const data = proxy.readFragment({ - fragment: COMMENT_FRAGMENT, - id: fragmentId - }); - - // Check whether we liked this comment. - const idx = data.action_summaries.findIndex(isReaction); - if ( - idx < 0 || - get(data.action_summaries[idx], 'current_user.id') !== id - ) { - return; - } - - data.action_summaries[idx] = { - ...data.action_summaries[idx], - count: data.action_summaries[idx].count - 1, - current_user: null - }; - - // Write our data back to the cache. - proxy.writeFragment({ - fragment: COMMENT_FRAGMENT, - id: fragmentId, - data - }); - } - }); - } - }) - } -); - -const withPostReaction = graphql( - gql` - mutation create${capitalize(name)}($${name}: Create${capitalize(name)}Input!) { - create${capitalize(name)}(${name}: $${capitalize(name)}) { - ${name} { - id - } - errors { - translation_key - } - } - } - `, - { - props: ({mutate}) => ({ - postReaction: reaction => { - return mutate({ - variables: {reaction}, - optimisticResponse: { - [`create${capitalize(name)}`]: { - __typename: `Create${capitalize(name)}Response`, - errors: null, - [name]: { - __typename: `${capitalize(name)}Action`, - id: 'pending' - } - } - }, - update: (proxy, mutationResult) => { - const fragmentId = `Comment_${reaction.item_id}`; - - // Read the data from our cache for this query. - const data = proxy.readFragment({ - fragment: COMMENT_FRAGMENT, - id: fragmentId - }); - - // Add our comment from the mutation to the end. - let idx = data.action_summaries.findIndex(isReaction); - - // Check whether we already reactioned this comment. - if (idx >= 0 && data.action_summaries[idx].current_user) { - return; - } - - if (idx < 0) { - // Add initial action when it doesn't exist. - data.action_summaries.push({ - __typename: `${capitalize(name)}ActionSummary`, - count: 0, - current_user: null - }); - idx = data.action_summaries.length - 1; - } - - data.action_summaries[idx] = { - ...data.action_summaries[idx], - count: data.action_summaries[idx].count + 1, - current_user: mutationResult.data[`create${capitalize(name)}`][name] - }; - - // Write our data back to the cache. - proxy.writeFragment({ - fragment: COMMENT_FRAGMENT, - id: fragmentId, - data - }); - } - }); - } - }) - } -); - -const mapDispatchToProps = dispatch => - bindActionCreators({showSignInDialog}, dispatch); - -const enhance = compose( - withFragments({ - root: gql` - fragment ${capitalize(name)}Button_root on RootQuery { - me { - status - } - } - `, - comment: gql` - fragment ${capitalize(name)}Button_comment on Comment { - action_summaries { - ... on ${capitalize(name)}ActionSummary { - count - current_user { - id - } - } - } - }` - }), - connect(null, mapDispatchToProps), - withDeleteReaction, - withPostReaction -); - -export default enhance(CoralReaction); diff --git a/client/coral-reaction/index.js b/client/coral-reaction/index.js deleted file mode 100644 index d8527b9fd..000000000 --- a/client/coral-reaction/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import CoralReaction from './containers/CoralReaction'; - -export default CoralReaction; \ No newline at end of file diff --git a/client/coral-reaction/reducer.js b/client/coral-reaction/reducer.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/coral-reaction/typeDefGenerator.js b/client/coral-reaction/typeDefGenerator.js deleted file mode 100644 index 63c20684e..000000000 --- a/client/coral-reaction/typeDefGenerator.js +++ /dev/null @@ -1,80 +0,0 @@ -export function typeDefGenerator(type = '') { - function capitalize(str) { - const newString = String.new(str); - return newString.charAt(0).toUpperCase() + newString.slice(1); - } - - return ` -enum ACTION_TYPE { - - # Represents a Like. - LIKE -} - -enum ASSET_METRICS_SORT { - - # Represents a LikeAction. - LIKE -} - -input CreateLikeInput { - - # The item's id for which we are to create a like. - item_id: ID! - - # The type of the item for which we are to create the like. - item_type: ACTION_ITEM_TYPE! -} - -# LikeAction is used by users who "like" a specific entity. -type LikeAction implements Action { - - # The ID of the action. - id: ID! - - # The author of the action. - user: User - - # The time when the Action was updated. - updated_at: Date - - # The time when the Action was created. - created_at: Date -} - -type LikeActionSummary implements ActionSummary { - - # The count of actions with this group. - count: Int - - # The current user's action. - current_user: LikeAction -} - -# A summary of counts related to all the Likes on an Asset. -type LikeAssetActionSummary implements AssetActionSummary { - - # Number of likes associated with actionable types on this this Asset. - actionCount: Int - - # Number of unique actionable types that are referenced by the likes. - actionableItemCount: Int -} - -type CreateLikeResponse implements Response { - - # The like that was created. - like: LikeAction - - # An array of errors relating to the mutation that occurred. - errors: [UserError] -} - -type RootMutation { - - # Creates a like on an entity. - createLike(like: CreateLikeInput!): CreateLikeResponse -} - -`; -} \ No newline at end of file From b7de01b0add69a6c1d54dc0f5e0a88ba41af6301 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 15:47:08 -0300 Subject: [PATCH 03/19] no console --- client/coral-framework/hocs/withReaction.js | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index b838941ad..ee478bb6d 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -138,7 +138,6 @@ export default reaction => WrappedComponent => { } }, update: (proxy, mutationResult) => { - console.log(isReaction); const fragmentId = `Comment_${reactionData.item_id}`; // Read the data from our cache for this query. @@ -149,7 +148,6 @@ export default reaction => WrappedComponent => { // Add our comment from the mutation to the end. let idx = data.action_summaries.findIndex(isReaction); - console.log(data.action_summaries); // Check whether we already reactioned this comment. if (idx >= 0 && data.action_summaries[idx].current_user) { @@ -194,21 +192,21 @@ export default reaction => WrappedComponent => { withFragments({ root: gql` fragment ${capitalize(reaction)}Button_root on RootQuery { - me { - status - } + me { + status + } } `, comment: gql` fragment ${capitalize(reaction)}Button_comment on Comment { - action_summaries { - ... on ${capitalize(reaction)}ActionSummary { - count - current_user { - id + action_summaries { + ... on ${capitalize(reaction)}ActionSummary { + count + current_user { + id + } } - } - } + } }` }), connect(null, mapDispatchToProps), From cd18a7e76ffbb87810c57b3a4a3b11b02cdeaf44 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 15:51:17 -0300 Subject: [PATCH 04/19] Fully working. replaced typename by typereaction. Thanks kiwi :joy: --- client/coral-framework/hocs/withReaction.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index ee478bb6d..992ab7854 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -36,7 +36,7 @@ export default reaction => WrappedComponent => { } const isReaction = a => - a.__typereaction === `${capitalize(reaction)}ActionSummary`; + a.__typename === `${capitalize(reaction)}ActionSummary`; const COMMENT_FRAGMENT = gql` fragment ${capitalize(reaction)}Button_updateFragment on Comment { @@ -68,7 +68,7 @@ export default reaction => WrappedComponent => { variables: {id}, optimisticResponse: { deleteAction: { - __typereaction: 'DeleteActionResponse', + __typename: 'DeleteActionResponse', errors: null } }, @@ -129,10 +129,10 @@ export default reaction => WrappedComponent => { variables: {[reaction]: reactionData}, optimisticResponse: { [`create${capitalize(reaction)}`]: { - __typereaction: `Create${capitalize(reaction)}Response`, + __typename: `Create${capitalize(reaction)}Response`, errors: null, [reaction]: { - __typereaction: `${capitalize(reaction)}Action`, + __typename: `${capitalize(reaction)}Action`, id: 'pending' } } @@ -157,7 +157,7 @@ export default reaction => WrappedComponent => { if (idx < 0) { // Add initial action when it doesn't exist. data.action_summaries.push({ - __typereaction: `${capitalize(reaction)}ActionSummary`, + __typename: `${capitalize(reaction)}ActionSummary`, count: 0, current_user: null }); From 6934263f712e06966d4f5b7b4b00b6bd7b39a3ad Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 15:59:52 -0300 Subject: [PATCH 05/19] =?UTF-8?q?=C3=81dding=20uuid=20v4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/coral-framework/hocs/withReaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index 992ab7854..db17f62d1 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -1,5 +1,6 @@ import React from 'react'; import get from 'lodash/get'; +import uuid from 'uuid/v4'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {compose, gql, graphql} from 'react-apollo'; @@ -133,7 +134,7 @@ export default reaction => WrappedComponent => { errors: null, [reaction]: { __typename: `${capitalize(reaction)}Action`, - id: 'pending' + id: uuid() } } }, From b547ad44f1267a22cc3db69f6ed09a35fda0ac07 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 17:46:56 -0300 Subject: [PATCH 06/19] Better and more friendly API --- client/coral-framework/hocs/withReaction.js | 32 +++++++++++++++---- .../client/components/LikeButton.js | 5 +-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index db17f62d1..5714a5a97 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -25,6 +25,7 @@ export default reaction => WrappedComponent => { `${capitalize(reaction)}ActionSummary`, comment ); + const count = getTotalActionCount( `${capitalize(reaction)}ActionSummary`, comment @@ -63,10 +64,21 @@ export default reaction => WrappedComponent => { } `, { - props: ({mutate}) => ({ - deleteReaction: (id, commentId) => { + props: ({mutate, ownProps}) => ({ + deleteReaction: () => { + + const reactionSummary = getMyActionSummary( + `${capitalize(reaction)}ActionSummary`, + ownProps.comment + ); + + const reactionData = { + id: reactionSummary.current_user.id, + commentId: ownProps.comment.id + }; + return mutate({ - variables: {id}, + variables: {id: reactionData.id}, optimisticResponse: { deleteAction: { __typename: 'DeleteActionResponse', @@ -74,7 +86,7 @@ export default reaction => WrappedComponent => { } }, update: proxy => { - const fragmentId = `Comment_${commentId}`; + const fragmentId = `Comment_${reactionData.commentId}`; // Read the data from our cache for this query. const data = proxy.readFragment({ @@ -86,7 +98,7 @@ export default reaction => WrappedComponent => { const idx = data.action_summaries.findIndex(isReaction); if ( idx < 0 || - get(data.action_summaries[idx], 'current_user.id') !== id + get(data.action_summaries[idx], 'current_user.id') !== reactionData.id ) { return; } @@ -124,8 +136,14 @@ export default reaction => WrappedComponent => { } `, { - props: ({mutate}) => ({ - postReaction: reactionData => { + props: ({mutate, ownProps}) => ({ + postReaction: () => { + + const reactionData = { + item_id: ownProps.comment.id, + item_type: 'COMMENTS' + }; + return mutate({ variables: {[reaction]: reactionData}, optimisticResponse: { diff --git a/plugins/coral-plugin-like/client/components/LikeButton.js b/plugins/coral-plugin-like/client/components/LikeButton.js index b91ffd42a..8e653825c 100644 --- a/plugins/coral-plugin-like/client/components/LikeButton.js +++ b/plugins/coral-plugin-like/client/components/LikeButton.js @@ -34,10 +34,7 @@ class LikeButton extends Component { if (myLikeActionSummary) { deleteAction(myLikeActionSummary.current_user.id, comment.id); } else { - postLike({ - item_id: comment.id, - item_type: 'COMMENTS' - }); + postLike(); } }; From b82cab06555cf8cf6ccc0e3730cf83d1af3cf903 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 17:50:15 -0300 Subject: [PATCH 07/19] Lint --- client/coral-framework/hocs/withReaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index 5714a5a97..bd641a885 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -174,6 +174,7 @@ export default reaction => WrappedComponent => { } if (idx < 0) { + // Add initial action when it doesn't exist. data.action_summaries.push({ __typename: `${capitalize(reaction)}ActionSummary`, From fe58a5076ed83ce45f8d2cb1911037ed5fe26a7f Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 18:05:39 -0300 Subject: [PATCH 08/19] Adding coral-plugin-love --- .gitignore | 1 + plugins/coral-plugin-love/client/.babelrc | 14 ++++ .../coral-plugin-love/client/.eslintrc.json | 23 ++++++ .../coral-plugin-love/client/LoveButton.js | 41 +++++++++++ plugins/coral-plugin-love/client/index.js | 7 ++ plugins/coral-plugin-love/client/styles.css | 26 +++++++ .../client/translations.json | 10 +++ plugins/coral-plugin-love/index.js | 37 ++++++++++ .../coral-plugin-love/server/typeDefs.graphql | 70 +++++++++++++++++++ 9 files changed, 229 insertions(+) create mode 100644 plugins/coral-plugin-love/client/.babelrc create mode 100644 plugins/coral-plugin-love/client/.eslintrc.json create mode 100644 plugins/coral-plugin-love/client/LoveButton.js create mode 100644 plugins/coral-plugin-love/client/index.js create mode 100644 plugins/coral-plugin-love/client/styles.css create mode 100644 plugins/coral-plugin-love/client/translations.json create mode 100644 plugins/coral-plugin-love/index.js create mode 100644 plugins/coral-plugin-love/server/typeDefs.graphql diff --git a/.gitignore b/.gitignore index a619f0988..e3ae90b9c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ plugins/* !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic !plugins/coral-plugin-like +!plugins/coral-plugin-love **/node_modules/* diff --git a/plugins/coral-plugin-love/client/.babelrc b/plugins/coral-plugin-love/client/.babelrc new file mode 100644 index 000000000..60be246eb --- /dev/null +++ b/plugins/coral-plugin-love/client/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} \ No newline at end of file diff --git a/plugins/coral-plugin-love/client/.eslintrc.json b/plugins/coral-plugin-love/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/coral-plugin-love/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js new file mode 100644 index 000000000..667c32a79 --- /dev/null +++ b/plugins/coral-plugin-love/client/LoveButton.js @@ -0,0 +1,41 @@ +import React from 'react'; +import withReaction from 'coral-framework/hocs/withReaction'; +import styles from './styles.css'; +import {Icon} from 'coral-ui'; + +class LoveButton extends React.Component { + handleClick = () => { + const { postReaction, deleteReaction, showSignInDialog, reactionSummary} = this.props; + const { root: { me }, comment } = this.props; + + // If the current user does not exist, trigger sign in dialog. + if (!me) { + showSignInDialog(); + return; + } + + // If the current user is banned, do nothing. + if (me.status === 'BANNED') { + return; + } + + if (reactionSummary) { + deleteReaction(); + } else { + postReaction(); + } + }; + + render() { + const {count, reactionSummary} = this.props; + return ( + + ); + } +} + +export default withReaction('love')(LoveButton); \ No newline at end of file diff --git a/plugins/coral-plugin-love/client/index.js b/plugins/coral-plugin-love/client/index.js new file mode 100644 index 000000000..f048963b4 --- /dev/null +++ b/plugins/coral-plugin-love/client/index.js @@ -0,0 +1,7 @@ +import LoveButton from './LoveButton'; + +export default { + slots: { + commentReactions: [LoveButton] + } +}; diff --git a/plugins/coral-plugin-love/client/styles.css b/plugins/coral-plugin-love/client/styles.css new file mode 100644 index 000000000..d48e7e28c --- /dev/null +++ b/plugins/coral-plugin-love/client/styles.css @@ -0,0 +1,26 @@ +.respect { + display: inline-block; +} + +.button { + color: #2a2a2a; + margin: 5px 10px 5px 0px; + background: none; + padding: 0px; + border: none; + font-size: inherit; + + &:hover { + color: #767676; + cursor: pointer; + } + + &.loved { + color: #e52338; + + &:hover { + color: #e52839; + cursor: pointer; + } + } +} diff --git a/plugins/coral-plugin-love/client/translations.json b/plugins/coral-plugin-love/client/translations.json new file mode 100644 index 000000000..93d73d3a2 --- /dev/null +++ b/plugins/coral-plugin-love/client/translations.json @@ -0,0 +1,10 @@ +{ + "en": { + "like": "Like", + "liked": "Liked" + }, + "es": { + "like": "Me Gusta", + "liked": "Me Gustó" + } +} diff --git a/plugins/coral-plugin-love/index.js b/plugins/coral-plugin-love/index.js new file mode 100644 index 000000000..48b4d16c0 --- /dev/null +++ b/plugins/coral-plugin-love/index.js @@ -0,0 +1,37 @@ +const {readFileSync} = require('fs'); +const path = require('path'); +const wrapResponse = require('../../graph/helpers/response'); + +module.exports = { + typeDefs: readFileSync(path.join(__dirname, 'server/typeDefs.graphql'), 'utf8'), + resolvers: { + RootMutation: { + createLove(_, {love: {item_id, item_type}}, {mutators: {Action}}) { + return wrapResponse('love')(Action.create({item_id, item_type, action_type: 'LOVE'})); + } + } + }, + hooks: { + Action: { + __resolveType: { + post({action_type}) { + switch (action_type) { + case 'LOVE': + return 'LoveAction'; + } + } + } + }, + ActionSummary: { + __resolveType: { + post({action_type}) { + switch (action_type) { + case 'LOVE': + return 'LoveActionSummary'; + } + } + } + } + } +}; + diff --git a/plugins/coral-plugin-love/server/typeDefs.graphql b/plugins/coral-plugin-love/server/typeDefs.graphql new file mode 100644 index 000000000..e22b91a2d --- /dev/null +++ b/plugins/coral-plugin-love/server/typeDefs.graphql @@ -0,0 +1,70 @@ +enum ACTION_TYPE { + + # Represents a Love. + LOVE +} + +enum ASSET_METRICS_SORT { + + # Represents a LoveAction. + LOVE +} + +input CreateLoveInput { + + # The item's id for which we are to create a love. + item_id: ID! + + # The type of the item for which we are to create the love. + item_type: ACTION_ITEM_TYPE! +} + +# LoveAction is used by users who "love" a specific entity. +type LoveAction implements Action { + + # The ID of the action. + id: ID! + + # The author of the action. + user: User + + # The time when the Action was updated. + updated_at: Date + + # The time when the Action was created. + created_at: Date +} + +type LoveActionSummary implements ActionSummary { + + # The count of actions with this group. + count: Int + + # The current user's action. + current_user: LoveAction +} + +# A summary of counts related to all the Loves on an Asset. +type LoveAssetActionSummary implements AssetActionSummary { + + # Number of loves associated with actionable types on this this Asset. + actionCount: Int + + # Number of unique actionable types that are referenced by the loves. + actionableItemCount: Int +} + +type CreateLoveResponse implements Response { + + # The love that was created. + love: LoveAction + + # An array of errors relating to the mutation that occurred. + errors: [UserError] +} + +type RootMutation { + + # Creates a love on an entity. + createLove(love: CreateLoveInput!): CreateLoveResponse +} From 88861754f175f20404f1a10dc5c6b6286d088186 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 8 May 2017 18:05:54 -0300 Subject: [PATCH 09/19] Adding full example with translations --- .../coral-plugin-love/client/LoveButton.js | 27 +++++++++++++------ .../client/translations.json | 8 +++--- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js index 667c32a79..260c5fc99 100644 --- a/plugins/coral-plugin-love/client/LoveButton.js +++ b/plugins/coral-plugin-love/client/LoveButton.js @@ -1,12 +1,20 @@ import React from 'react'; -import withReaction from 'coral-framework/hocs/withReaction'; -import styles from './styles.css'; import {Icon} from 'coral-ui'; +import styles from './styles.css'; +import {I18n} from 'coral-framework'; +import translations from './translations.json'; +import withReaction from 'coral-framework/hocs/withReaction'; +const lang = new I18n(translations); class LoveButton extends React.Component { handleClick = () => { - const { postReaction, deleteReaction, showSignInDialog, reactionSummary} = this.props; - const { root: { me }, comment } = this.props; + const { + postReaction, + deleteReaction, + showSignInDialog, + reactionSummary + } = this.props; + const {root: {me}, comment} = this.props; // If the current user does not exist, trigger sign in dialog. if (!me) { @@ -29,13 +37,16 @@ class LoveButton extends React.Component { render() { const {count, reactionSummary} = this.props; return ( - ); } } -export default withReaction('love')(LoveButton); \ No newline at end of file +export default withReaction('love')(LoveButton); diff --git a/plugins/coral-plugin-love/client/translations.json b/plugins/coral-plugin-love/client/translations.json index 93d73d3a2..a015efa77 100644 --- a/plugins/coral-plugin-love/client/translations.json +++ b/plugins/coral-plugin-love/client/translations.json @@ -1,10 +1,10 @@ { "en": { - "like": "Like", - "liked": "Liked" + "love": "Love", + "loved": "Loved" }, "es": { - "like": "Me Gusta", - "liked": "Me Gustó" + "love": "Amo", + "loved": "Amé" } } From 788610a5f6f82f2f866006cc6f0334a3bd746aec Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 9 May 2017 09:02:04 -0300 Subject: [PATCH 10/19] Like button params --- plugins/coral-plugin-like/client/components/LikeButton.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/coral-plugin-like/client/components/LikeButton.js b/plugins/coral-plugin-like/client/components/LikeButton.js index 8e653825c..b91ffd42a 100644 --- a/plugins/coral-plugin-like/client/components/LikeButton.js +++ b/plugins/coral-plugin-like/client/components/LikeButton.js @@ -34,7 +34,10 @@ class LikeButton extends Component { if (myLikeActionSummary) { deleteAction(myLikeActionSummary.current_user.id, comment.id); } else { - postLike(); + postLike({ + item_id: comment.id, + item_type: 'COMMENTS' + }); } }; From 3c06ee9360f6595319454c7a9f555ee65ec4fc16 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 9 May 2017 12:21:21 -0300 Subject: [PATCH 11/19] Adds slots to admin stream --- .../ModerationQueue/components/Comment.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js index e5afd9726..8726cc75c 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js @@ -1,16 +1,17 @@ import React, {PropTypes} from 'react'; import timeago from 'timeago.js'; +import {Link} from 'react-router'; import Linkify from 'react-linkify'; import Highlighter from 'react-highlight-words'; -import {Link} from 'react-router'; import styles from './styles.css'; import {Icon} from 'coral-ui'; import FlagBox from './FlagBox'; import CommentType from './CommentType'; +import Slot from 'coral-framework/components/Slot'; +import {getActionSummary} from 'coral-framework/utils'; import ActionButton from 'coral-admin/src/components/ActionButton'; import BanUserButton from 'coral-admin/src/components/BanUserButton'; -import {getActionSummary} from 'coral-framework/utils'; const linkify = new Linkify(); @@ -50,6 +51,10 @@ const Comment = ({actions = [], comment, ...props}) => { {lang.t('comment.banned_user')} : null} +
Story: {comment.asset.title} @@ -62,6 +67,10 @@ const Comment = ({actions = [], comment, ...props}) => { +

{links ? Contains Link : null} @@ -78,9 +87,19 @@ const Comment = ({actions = [], comment, ...props}) => { rejectComment={() => props.rejectComment({commentId: comment.id})} />; })}
+
+
+ +
{ flagActions && flagActions.length ? From 52b0819e7874802182dfa317efb761927a26c81c Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 9 May 2017 14:02:35 -0300 Subject: [PATCH 12/19] Adding example for plugins within the Admin Comment Queue --- .gitignore | 1 + plugins/coral-plugin-mod/client/.babelrc | 14 ++++++++ .../coral-plugin-mod/client/.eslintrc.json | 23 +++++++++++++ .../coral-plugin-mod/client/components/Box.js | 8 +++++ .../client/components/Container.js | 32 +++++++++++++++++++ .../client/components/styles.css | 8 +++++ plugins/coral-plugin-mod/client/index.js | 7 ++++ plugins/coral-plugin-mod/index.js | 4 +++ 8 files changed, 97 insertions(+) create mode 100644 plugins/coral-plugin-mod/client/.babelrc create mode 100644 plugins/coral-plugin-mod/client/.eslintrc.json create mode 100644 plugins/coral-plugin-mod/client/components/Box.js create mode 100644 plugins/coral-plugin-mod/client/components/Container.js create mode 100644 plugins/coral-plugin-mod/client/components/styles.css create mode 100644 plugins/coral-plugin-mod/client/index.js create mode 100644 plugins/coral-plugin-mod/index.js diff --git a/.gitignore b/.gitignore index a619f0988..9f04eb4b8 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ plugins/* !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic !plugins/coral-plugin-like +!plugins/coral-plugin-mod **/node_modules/* diff --git a/plugins/coral-plugin-mod/client/.babelrc b/plugins/coral-plugin-mod/client/.babelrc new file mode 100644 index 000000000..60be246eb --- /dev/null +++ b/plugins/coral-plugin-mod/client/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} \ No newline at end of file diff --git a/plugins/coral-plugin-mod/client/.eslintrc.json b/plugins/coral-plugin-mod/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/coral-plugin-mod/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/coral-plugin-mod/client/components/Box.js b/plugins/coral-plugin-mod/client/components/Box.js new file mode 100644 index 000000000..15f5320b1 --- /dev/null +++ b/plugins/coral-plugin-mod/client/components/Box.js @@ -0,0 +1,8 @@ +import React from 'react'; +import styles from './styles.css' + +export default (props) => ( +
+ Comment Status: {props.comment.status} +
+) \ No newline at end of file diff --git a/plugins/coral-plugin-mod/client/components/Container.js b/plugins/coral-plugin-mod/client/components/Container.js new file mode 100644 index 000000000..712031fdf --- /dev/null +++ b/plugins/coral-plugin-mod/client/components/Container.js @@ -0,0 +1,32 @@ +import React from 'react'; +import Box from './Box'; +import {Button} from 'coral-ui' +import styles from './styles.css'; + +export default class Footer extends React.Component { + constructor() { + super(); + + this.state = { + show: false + }; + } + + handleClick = () => { + this.setState(state => ({ + show: !state.show + })) + } + + render() { + const {show} = this.state; + return ( +
+ + {show ? : null} +
+ ); + } +} diff --git a/plugins/coral-plugin-mod/client/components/styles.css b/plugins/coral-plugin-mod/client/components/styles.css new file mode 100644 index 000000000..59bb63c3d --- /dev/null +++ b/plugins/coral-plugin-mod/client/components/styles.css @@ -0,0 +1,8 @@ +.container { + padding: 0 14px 10px; +} + +.box { + font-size: 12px; + padding: 10px 14px; +} \ No newline at end of file diff --git a/plugins/coral-plugin-mod/client/index.js b/plugins/coral-plugin-mod/client/index.js new file mode 100644 index 000000000..b0ea304f4 --- /dev/null +++ b/plugins/coral-plugin-mod/client/index.js @@ -0,0 +1,7 @@ +import Container from './components/Container'; + +export default { + slots: { + adminCommentDetailArea: [Container], + } +}; diff --git a/plugins/coral-plugin-mod/index.js b/plugins/coral-plugin-mod/index.js new file mode 100644 index 000000000..42d90e144 --- /dev/null +++ b/plugins/coral-plugin-mod/index.js @@ -0,0 +1,4 @@ +const {readFileSync} = require('fs'); +const path = require('path'); + +module.exports = {}; From 3e1b7591e006091ffa91b4368b1f4f1f1f54eadb Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 10 May 2017 14:07:58 -0300 Subject: [PATCH 13/19] Adds displayName for WrappedReactions and alreadyReacted --- client/coral-framework/helpers/hoc.js | 3 +++ client/coral-framework/hocs/withFragments.js | 6 +----- client/coral-framework/hocs/withReaction.js | 8 +++++++- plugins/coral-plugin-love/client/LoveButton.js | 10 +++++----- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/client/coral-framework/helpers/hoc.js b/client/coral-framework/helpers/hoc.js index e69de29bb..bb034b179 100644 --- a/client/coral-framework/helpers/hoc.js +++ b/client/coral-framework/helpers/hoc.js @@ -0,0 +1,3 @@ +export function getDisplayName(WrappedComponent) { + return WrappedComponent.displayName || WrappedComponent.name || 'Component'; +} diff --git a/client/coral-framework/hocs/withFragments.js b/client/coral-framework/hocs/withFragments.js index a2686d94b..a5060f27a 100644 --- a/client/coral-framework/hocs/withFragments.js +++ b/client/coral-framework/hocs/withFragments.js @@ -1,11 +1,7 @@ import React from 'react'; +import {getDisplayName} from '../helpers/hoc'; // TODO: revisit `filtering` after https://github.com/apollographql/graphql-anywhere/issues/38. - -function getDisplayName(WrappedComponent) { - return WrappedComponent.displayName || WrappedComponent.name || 'Component'; -} - export default fragments => WrappedComponent => { class WithFragments extends React.Component { render() { diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index bd641a885..393d6db3a 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -3,6 +3,7 @@ import get from 'lodash/get'; import uuid from 'uuid/v4'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; +import {getDisplayName} from '../helpers/hoc'; import {compose, gql, graphql} from 'react-apollo'; import withFragments from 'coral-framework/hocs/withFragments'; import {showSignInDialog} from 'coral-framework/actions/auth'; @@ -31,7 +32,10 @@ export default reaction => WrappedComponent => { comment ); - const withReactionProps = {reactionSummary, count}; + const alreadyReacted = () => !!reactionSummary; + + const withReactionProps = {reactionSummary, count, alreadyReacted}; + return ; } @@ -234,5 +238,7 @@ export default reaction => WrappedComponent => { withPostReaction ); + WithReactions.displayName = `WithReactions(${getDisplayName(WrappedComponent)})`; + return enhance(WithReactions); }; diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js index 260c5fc99..383873f63 100644 --- a/plugins/coral-plugin-love/client/LoveButton.js +++ b/plugins/coral-plugin-love/client/LoveButton.js @@ -12,7 +12,7 @@ class LoveButton extends React.Component { postReaction, deleteReaction, showSignInDialog, - reactionSummary + alreadyReacted } = this.props; const {root: {me}, comment} = this.props; @@ -27,7 +27,7 @@ class LoveButton extends React.Component { return; } - if (reactionSummary) { + if (alreadyReacted()) { deleteReaction(); } else { postReaction(); @@ -35,13 +35,13 @@ class LoveButton extends React.Component { }; render() { - const {count, reactionSummary} = this.props; + const {count, alreadyReacted} = this.props; return ( From 26e5631f5214096d64b40587718fd344b65e28cd Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 10 May 2017 14:13:32 -0300 Subject: [PATCH 14/19] Changing path for hocs --- client/coral-framework/hocs/index.js | 7 +++++++ client/coral-framework/index.js | 2 ++ plugins/coral-plugin-love/client/LoveButton.js | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 client/coral-framework/hocs/index.js diff --git a/client/coral-framework/hocs/index.js b/client/coral-framework/hocs/index.js new file mode 100644 index 000000000..6b05553d9 --- /dev/null +++ b/client/coral-framework/hocs/index.js @@ -0,0 +1,7 @@ +import withFragments from './withFragments'; +import withReaction from './withReaction'; + +export default { + withFragments, + withReaction +} \ No newline at end of file diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js index b85b69bcd..46ee704f4 100644 --- a/client/coral-framework/index.js +++ b/client/coral-framework/index.js @@ -1,6 +1,7 @@ import pym from './services/PymConnection'; import I18n from './modules/i18n/i18n'; import actions from './actions'; +import hocs from './hocs'; // TODO (bc): Deprecate old actions. Spreading actions is now needed. @@ -8,5 +9,6 @@ export default { pym, I18n, actions, + ...hocs, ...actions }; diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js index 383873f63..b85d7280b 100644 --- a/plugins/coral-plugin-love/client/LoveButton.js +++ b/plugins/coral-plugin-love/client/LoveButton.js @@ -3,7 +3,7 @@ import {Icon} from 'coral-ui'; import styles from './styles.css'; import {I18n} from 'coral-framework'; import translations from './translations.json'; -import withReaction from 'coral-framework/hocs/withReaction'; +import {withReaction} from 'coral-framework'; const lang = new I18n(translations); class LoveButton extends React.Component { From 644d00d5d8ac7fc9174567269959a8577a8efd31 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 10 May 2017 14:57:24 -0300 Subject: [PATCH 15/19] Adding coral-plugin-api --- client/coral-plugin-api/index.js | 5 +++++ plugins/coral-plugin-love/client/LoveButton.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 client/coral-plugin-api/index.js diff --git a/client/coral-plugin-api/index.js b/client/coral-plugin-api/index.js new file mode 100644 index 000000000..8aa03205b --- /dev/null +++ b/client/coral-plugin-api/index.js @@ -0,0 +1,5 @@ +import hocs from '../coral-framework/hocs'; + +export default { + ...hocs +}; diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js index b85d7280b..4364c4818 100644 --- a/plugins/coral-plugin-love/client/LoveButton.js +++ b/plugins/coral-plugin-love/client/LoveButton.js @@ -3,7 +3,7 @@ import {Icon} from 'coral-ui'; import styles from './styles.css'; import {I18n} from 'coral-framework'; import translations from './translations.json'; -import {withReaction} from 'coral-framework'; +import {withReaction} from 'coral-plugin-api'; const lang = new I18n(translations); class LoveButton extends React.Component { From 2673de5059799ceb630eeef67b2ed9d57dd5bce6 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 10 May 2017 14:59:10 -0300 Subject: [PATCH 16/19] lintin --- client/coral-framework/hocs/index.js | 2 +- client/coral-framework/hocs/withReaction.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/coral-framework/hocs/index.js b/client/coral-framework/hocs/index.js index 6b05553d9..831c356d2 100644 --- a/client/coral-framework/hocs/index.js +++ b/client/coral-framework/hocs/index.js @@ -4,4 +4,4 @@ import withReaction from './withReaction'; export default { withFragments, withReaction -} \ No newline at end of file +}; diff --git a/client/coral-framework/hocs/withReaction.js b/client/coral-framework/hocs/withReaction.js index 393d6db3a..c383dbed8 100644 --- a/client/coral-framework/hocs/withReaction.js +++ b/client/coral-framework/hocs/withReaction.js @@ -36,7 +36,6 @@ export default reaction => WrappedComponent => { const withReactionProps = {reactionSummary, count, alreadyReacted}; - return ; } } From a3a8d2379759eda4a52f23f0704ec0609226ea50 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 10 May 2017 12:18:42 -0600 Subject: [PATCH 17/19] patched bug with adding action --- services/comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/comments.js b/services/comments.js index d77118705..d51caefcf 100644 --- a/services/comments.js +++ b/services/comments.js @@ -240,7 +240,7 @@ module.exports = class CommentsService { * @param {String} action the new action to the comment * @return {Promise} */ - static addAction(item_id, user_id, action_type, metadata) { + static addAction(item_id, user_id, action_type, metadata = {}) { return ActionsService.insertUserAction({ item_id, item_type: 'COMMENTS', From 0a2b26b599590f6098aecf23cc7ec714ab08cc13 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Wed, 10 May 2017 15:53:33 -0300 Subject: [PATCH 18/19] Exports --- client/coral-plugin-api/index.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/coral-plugin-api/index.js b/client/coral-plugin-api/index.js index 8aa03205b..f0f5ad9cc 100644 --- a/client/coral-plugin-api/index.js +++ b/client/coral-plugin-api/index.js @@ -1,5 +1 @@ -import hocs from '../coral-framework/hocs'; - -export default { - ...hocs -}; +export {withReaction} from '../coral-framework/hocs'; From ef502bf7a86cb280c71803651e7634544e4f95b7 Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Thu, 11 May 2017 09:46:04 -0400 Subject: [PATCH 19/19] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 41cbbd5aa..597d8fa73 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2016 Mozilla Foundation +Copyright 2017 Mozilla Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.