From 8b3f69d964580f8ce1e1af8d7434ef056f2b92d7 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 5 May 2017 22:40:54 +0700 Subject: [PATCH 01/16] Use getDisplayName from recompose --- client/coral-framework/hocs/withFragments.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/coral-framework/hocs/withFragments.js b/client/coral-framework/hocs/withFragments.js index a2686d94b..bcc362ca3 100644 --- a/client/coral-framework/hocs/withFragments.js +++ b/client/coral-framework/hocs/withFragments.js @@ -1,11 +1,8 @@ import React from 'react'; +import {getDisplayName} from 'recompose'; // 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() { From 1b7eedfd8d3228d44209703ec0edd3c9cb6a5add Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 5 May 2017 22:51:19 +0700 Subject: [PATCH 02/16] Implement extendable GraphQL framework --- .../src/components/Comment.js | 8 +- .../src/components/Stream.js | 10 +- .../src/containers/Comment.js | 2 +- .../src/containers/Embed.js | 10 +- .../src/containers/Stream.js | 6 +- .../coral-embed-stream/src/graphql/index.js | 105 +++++++++++ client/coral-embed-stream/src/index.js | 1 + .../graphql/fragments/index.js | 2 + .../graphql/mutations/index.js | 95 +++------- .../graphql/mutations/postComment.graphql | 16 -- client/coral-framework/helpers/plugins.js | 23 ++- client/coral-framework/hocs/index.js | 3 + client/coral-framework/hocs/withMutation.js | 74 ++++++++ client/coral-framework/hocs/withQuery.js | 34 ++++ client/coral-framework/services/registry.js | 171 ++++++++++++++++++ client/coral-plugin-commentbox/CommentBox.js | 6 +- client/coral-plugin-replies/ReplyBox.js | 6 +- .../client/containers/RespectButton.js | 18 +- 18 files changed, 463 insertions(+), 127 deletions(-) create mode 100644 client/coral-embed-stream/src/graphql/index.js create mode 100644 client/coral-framework/graphql/fragments/index.js delete mode 100644 client/coral-framework/graphql/mutations/postComment.graphql create mode 100644 client/coral-framework/hocs/index.js create mode 100644 client/coral-framework/hocs/withMutation.js create mode 100644 client/coral-framework/hocs/withQuery.js create mode 100644 client/coral-framework/services/registry.js diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index 7ce143c55..87412c0a6 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -54,7 +54,7 @@ class Comment extends React.Component { parentId: PropTypes.string, highlighted: PropTypes.string, addNotification: PropTypes.func.isRequired, - postItem: PropTypes.func.isRequired, + postComment: PropTypes.func.isRequired, depth: PropTypes.number.isRequired, asset: PropTypes.shape({ id: PropTypes.string, @@ -107,7 +107,7 @@ class Comment extends React.Component { currentUser, asset, depth, - postItem, + postComment, addNotification, showSignInDialog, postLike, @@ -268,7 +268,7 @@ class Comment extends React.Component { parentId={parentId || comment.id} addNotification={addNotification} authorId={currentUser.id} - postItem={postItem} + postComment={postComment} assetId={asset.id} /> : null } @@ -285,7 +285,7 @@ class Comment extends React.Component { activeReplyBox={activeReplyBox} addNotification={addNotification} parentId={comment.id} - postItem={postItem} + postComment={postComment} depth={depth + 1} asset={asset} highlighted={highlighted} diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index eb30dc16c..96e571a01 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -26,7 +26,7 @@ class Stream extends React.Component { render() { const { root: {asset, asset: {comments}, comment, myIgnoredUsers}, - postItem, + postComment, addNotification, postFlag, postLike, @@ -85,7 +85,7 @@ class Stream extends React.Component { {user ? ({ variables: { assetId, @@ -85,7 +86,6 @@ export const withQuery = graphql(EMBED_QUERY, { excludeIgnored: Boolean(auth && auth.user && auth.user.id), }, }), - props: ({data}) => separateDataAndRoot(data), }); const mapStateToProps = state => ({ @@ -113,6 +113,6 @@ export default compose( props => !props.auth.checkedInitialLogin, renderComponent(Spinner), ), - withQuery, + withEmbedQuery, )(EmbedContainer); diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 49f234dab..2253a6a82 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -6,13 +6,13 @@ import uniqBy from 'lodash/uniqBy'; import sortBy from 'lodash/sortBy'; import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; -import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; +import {withPostComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; import {setCommentCountCache, setActiveReplyBox} from '../actions/stream'; import Stream from '../components/Stream'; import Comment from './Comment'; -import withFragments from 'coral-framework/hocs/withFragments'; +import {withFragments} from 'coral-framework/hocs'; import {getDefinitionName} from 'coral-framework/utils'; const {showSignInDialog} = authActions; @@ -237,7 +237,7 @@ const mapDispatchToProps = dispatch => export default compose( withFragments(fragments), connect(mapStateToProps, mapDispatchToProps), - postComment, + withPostComment, postFlag, postLike, postDontAgree, diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js new file mode 100644 index 000000000..2d6f943a2 --- /dev/null +++ b/client/coral-embed-stream/src/graphql/index.js @@ -0,0 +1,105 @@ +import {gql} from 'react-apollo'; +import {registerConfig} from 'coral-framework/services/registry'; + +const config = { + fragments: { + CreateCommentResponse: gql` + fragment Coral_CreateCommentResponse on CreateCommentResponse { + comment { + ...Coral_CreateCommentResponse_Comment + replies { + ...Coral_CreateCommentResponse_Comment + } + } + errors { + translation_key + } + } + + fragment Coral_CreateCommentResponse_Comment on Comment { + id + body + created_at + status + replyCount + tags { + name + } + user { + id + name: username + } + action_summaries { + count + current_user { + id + created_at + } + } + }`, + }, + mutations: { + PostComment: ({ + variables: {comment: {asset_id, body, parent_id, tags = []}}, + state: {auth}, + }) => ({ + optimisticResponse: { + createComment: { + comment: { + user: { + id: auth.toJS().user.id, + name: auth.toJS().user.username + }, + created_at: new Date().toISOString(), + body, + parent_id, + asset_id, + action_summaries: [], + tags, + status: null, + id: 'pending' + } + } + }, + updateQueries: { + EmbedQuery: (oldData, {mutationResult: {data: {createComment: {comment}}}}) => { + if (oldData.asset.settings.moderation === 'PRE' || comment.status === 'PREMOD' || comment.status === 'REJECTED') { + return oldData; + } + + let updatedAsset; + + // If posting a reply + if (parent_id) { + updatedAsset = { + ...oldData, + asset: { + ...oldData.asset, + comments: oldData.asset.comments.map((oldComment) => { + return oldComment.id === parent_id + ? {...oldComment, replies: [...oldComment.replies, comment], replyCount: oldComment.replyCount + 1} + : oldComment; + }) + } + }; + } else { + + // If posting a top-level comment + updatedAsset = { + ...oldData, + asset: { + ...oldData.asset, + commentCount: oldData.asset.commentCount + 1, + comments: [comment, ...oldData.asset.comments] + } + }; + } + + return updatedAsset; + } + } + }) + }, +}; + +registerConfig(config); diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js index 2fd1e2731..bd63ba267 100644 --- a/client/coral-embed-stream/src/index.js +++ b/client/coral-embed-stream/src/index.js @@ -4,6 +4,7 @@ import {ApolloProvider} from 'react-apollo'; import {client} from 'coral-framework/services/client'; import {checkLogin} from 'coral-framework/actions/auth'; +import './graphql'; import reducers from './reducers'; import localStore, {injectReducers} from 'coral-framework/services/store'; diff --git a/client/coral-framework/graphql/fragments/index.js b/client/coral-framework/graphql/fragments/index.js new file mode 100644 index 000000000..f4948ed05 --- /dev/null +++ b/client/coral-framework/graphql/fragments/index.js @@ -0,0 +1,2 @@ +// fragments defined here are automatically registered. +export default {}; diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index b1ea57ae1..56f63b372 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ -import {graphql} from 'react-apollo'; -import POST_COMMENT from './postComment.graphql'; +import {graphql, gql} from 'react-apollo'; import POST_FLAG from './postFlag.graphql'; import POST_LIKE from './postLike.graphql'; import POST_DONT_AGREE from './postDontAgree.graphql'; @@ -8,80 +7,28 @@ import ADD_COMMENT_TAG from './addCommentTag.graphql'; import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; +import withMutation from '../../hocs/withMutation'; +import {getFragmentDocument} from '../../services/registry'; -import commentView from '../fragments/commentView.graphql'; - -export const postComment = graphql(POST_COMMENT, { - options: () => ({ - fragments: commentView - }), - props: ({ownProps, mutate}) => ({ - postItem: comment => { - const {asset_id, body, parent_id, tags = []} = comment; - return mutate({ - variables: { - comment - }, - optimisticResponse: { - createComment: { - comment: { - user: { - id: ownProps.auth.user.id, - name: ownProps.auth.user.username - }, - created_at: new Date().toISOString(), - body, - parent_id, - asset_id, - action_summaries: [], - tags, - status: null, - id: 'pending' - } - } - }, - updateQueries: { - EmbedQuery: (oldData, {mutationResult: {data: {createComment: {comment}}}}) => { - - if (oldData.asset.settings.moderation === 'PRE' || comment.status === 'PREMOD' || comment.status === 'REJECTED') { - return oldData; - } - - let updatedAsset; - - // If posting a reply - if (parent_id) { - updatedAsset = { - ...oldData, - asset: { - ...oldData.asset, - comments: oldData.asset.comments.map((oldComment) => { - return oldComment.id === parent_id - ? {...oldComment, replies: [...oldComment.replies, comment], replyCount: oldComment.replyCount + 1} - : oldComment; - }) - } - }; - } else { - - // If posting a top-level comment - updatedAsset = { - ...oldData, - asset: { - ...oldData.asset, - commentCount: oldData.asset.commentCount + 1, - comments: [comment, ...oldData.asset.comments] - } - }; - } - - return updatedAsset; - } - } - }); +export const withPostComment = withMutation( + gql` + mutation PostComment($comment: CreateCommentInput!) { + createComment(comment: $comment) { + ...CreateCommentResponse + } } - }), -}); + ${getFragmentDocument('CreateCommentResponse')} + `, { + props: ({mutate}) => ({ + postComment: comment => { + return mutate({ + variables: { + comment + }, + }); + } + }), + }); export const postLike = graphql(POST_LIKE, { props: ({mutate}) => ({ diff --git a/client/coral-framework/graphql/mutations/postComment.graphql b/client/coral-framework/graphql/mutations/postComment.graphql deleted file mode 100644 index f98558804..000000000 --- a/client/coral-framework/graphql/mutations/postComment.graphql +++ /dev/null @@ -1,16 +0,0 @@ -#import "../fragments/commentView.graphql" - -mutation CreateComment ($comment: CreateCommentInput!) { - createComment(comment: $comment) { - comment { - ...commentView - replyCount - replies { - ...commentView - } - } - errors { - translation_key - } - } -} diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index 20aee7abb..76995c204 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -3,6 +3,7 @@ import merge from 'lodash/merge'; import flatten from 'lodash/flatten'; import flattenDeep from 'lodash/flattenDeep'; import uniq from 'lodash/uniq'; +import pick from 'lodash/pick'; import plugins from 'pluginsConfig'; import {gql} from 'react-apollo'; import {getDefinitionName} from 'coral-framework/utils'; @@ -25,19 +26,27 @@ export function getSlotElements(slot, props = {}) { } function getComponentFragments(components) { - return components + const res = components .map(c => c.fragments) .filter(fragments => fragments) .reduce((res, fragments) => { Object.keys(fragments).forEach(key => { if (!(key in res)) { - res[key] = {spreads: '', definitions: ''}; + res[key] = {spreads: [], definitions: []}; } - res[key].spreads += `...${getDefinitionName(fragments[key])}\n`; - res[key].definitions = gql`${res[key].definitions}${fragments[key]}`; + res[key].spreads.push(getDefinitionName(fragments[key])); + res[key].definitions.push(fragments[key]); }); return res; }, {}); + + Object.keys(res).forEach(key => { + res[key].spreads = `...${res[key].spreads.join('\n...')}\n`; + const literals = ['', ...res[key].definitions.map(() => '\n')]; + res[key].definitions = gql.apply(null, [literals, ...res[key].definitions]); + }); + + return res; } /** @@ -73,3 +82,9 @@ export function getSlotsFragments(slots) { }; } +export function getGraphQLConfigs() { + return plugins + .map(o => o.module.mutations && pick(o.module, ['mutations', 'queries', 'fragments'])) + .filter(o => o); +} + diff --git a/client/coral-framework/hocs/index.js b/client/coral-framework/hocs/index.js new file mode 100644 index 000000000..d624a3308 --- /dev/null +++ b/client/coral-framework/hocs/index.js @@ -0,0 +1,3 @@ +export {default as withFragments} from './withFragments'; +export {default as withMutation} from './withMutation'; +export {default as withQuery} from './withQuery'; diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js new file mode 100644 index 000000000..423ab9793 --- /dev/null +++ b/client/coral-framework/hocs/withMutation.js @@ -0,0 +1,74 @@ +import {graphql} from 'react-apollo'; +import merge from 'lodash/merge'; +import uniq from 'lodash/uniq'; +import {getMutationOptions} from 'coral-framework/services/registry'; +import {store} from 'coral-framework/services/store'; +import {getDefinitionName} from '../utils'; + +export default (definitions, config) => WrappedComponent => { + config = { + ...config, + options: config.options || {}, + props: config.props || (data => ({mutate: data.mutate()})), + }; + const wrappedProps = (data) => { + const name = getDefinitionName(definitions); + const callbacks = getMutationOptions(name); + const mutate = (base) => { + const variables = base.variables || config.options.variables; + const configs = callbacks.map(cb => cb({variables, state: store.getState()})); + + const optimisticResponse = merge( + base.optimisticResponse || config.options.optimisticResponse, + ...configs.map(cfg => cfg.optimisticResponse), + ); + + const refetchQueries = uniq( + base.refetchQueries || config.options.refetchQueries, + ...configs.map(cfg => cfg.refetchQueries), + ); + + const updateCallbacks = + [base.update || config.options.update] + .concat(...configs.map(cfg => cfg.update)) + .filter(i => i); + + const update = (proxy, result) => { + updateCallbacks.forEach(cb => cb(proxy, result)); + }; + + const updateQueries = + [ + base.updateQueries || config.options.updateQueries, + ...configs.map(cfg => cfg.updateQueries) + ] + .filter(i => i) + .reduce((res, map) => { + Object.keys(map).forEach(key => { + if (!(key in res)) { + res[key] = map[key]; + } else { + const existing = res[key]; + res[key] = (prev, result) => { + const next = existing(prev, result); + return map[key](next, result); + }; + } + }); + return res; + }, {}); + + const wrappedConfig = { + variables, + optimisticResponse, + refetchQueries, + updateQueries, + update, + }; + return data.mutate(wrappedConfig); + }; + return config.props({...data, mutate}); + }; + const wrapped = graphql(definitions, {...config, props: wrappedProps})(WrappedComponent); + return wrapped; +}; diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js new file mode 100644 index 000000000..9677c223e --- /dev/null +++ b/client/coral-framework/hocs/withQuery.js @@ -0,0 +1,34 @@ +import {graphql} from 'react-apollo'; +import {getQueryOptions} from 'coral-framework/services/registry'; +import {getDefinitionName, separateDataAndRoot} from '../utils'; + +export default (definitions, config) => WrappedComponent => { + config = { + ...config, + options: config.options || {}, + props: config.props || (({data}) => separateDataAndRoot(data)), + }; + + const wrappedOptions = (data) => { + const base = (typeof config.options === 'function') ? config.options(data) : config.options; + const name = getDefinitionName(definitions); + const configs = getQueryOptions(name); + const reducerCallbacks = + [base.reducer || (i => i)] + .concat(...configs.map(cfg => cfg.reducer)) + .filter(i => i); + + const reducer = reducerCallbacks.reduce( + (a, b) => (prev, ...rest) => + b(a(prev, ...rest), ...rest), + ); + + return { + ...base, + reducer, + }; + }; + + const wrapped = graphql(definitions, {...config, options: wrappedOptions})(WrappedComponent); + return wrapped; +}; diff --git a/client/coral-framework/services/registry.js b/client/coral-framework/services/registry.js new file mode 100644 index 000000000..2e889de94 --- /dev/null +++ b/client/coral-framework/services/registry.js @@ -0,0 +1,171 @@ +import {gql} from 'react-apollo'; +import {getDefinitionName} from 'coral-framework/utils'; +import {getGraphQLConfigs} from 'coral-framework/helpers/plugins'; +import globalFragments from 'coral-framework/graphql/fragments'; + +const fragments = {}; +const mutationOptions = {}; +const queryOptions = {}; + +const getTypeName = (ast) => ast.definitions[0].typeCondition.name.value; + +/** + * Register fragment + * + * Example: + * registerFragment('MyFragment', gql` + * fragment Plugin_MyFragment on Comment { + * body + * } + * `); + */ +export function registerFragment(key, document) { + const type = getTypeName(document); + const name = getDefinitionName(document); + if (!(key in fragments)) { + fragments[key] = {type, names: [name], documents: [document]}; + } else { + if (type !== fragments[key].type) { + console.error(`Type mismatch ${type} !== ${fragments[key].type}`); + } + fragments[key].names.push(name); + fragments[key].documents.push(document); + } +} + +/** + * Register mutation options. + * + * Example: + * registerMutationOptions('PostComment', ({variables, state}) => ({ + * optimisticResponse: { + * CreateComment: { + * extra: '', + * }, + * }, + * refetchQueries: [], + * updateQueries: { + * EmbedQuery: (previous, data) => { + * return previous; + * }, + * }, + * update: (proxy, result) => { + * }, + * }) + */ +export function registerMutationOptions(key, config) { + if (!(key in mutationOptions)) { + mutationOptions[key] = [config]; + } else { + mutationOptions[key].push(config); + } +} + +/** + * Register query options. + * + * Example: + * registerQueryOptions('EmbedQuery', { + * reducer: (previousResult, action, variables) => previousResult, + * }); + */ +export function registerQueryOptions(key, config) { + if (!(key in queryOptions)) { + queryOptions[key] = [config]; + } else { + queryOptions[key].push(config); + } +} + +/** + * Register all fragments, mutation options, and query options defined in the object. + * + * Example: + * registerConfig({ + * fragments: { + * CreateCommentResponse: gql` + * fragment CoralRandomEmoji_CreateCommentResponse on CreateCommentResponse { + * [...] + * }`, + * }, + * mutations: { + * PostComment: ({variables, state}) => ({ + * optimisticResponse: { + * [...] + * }, + * refetchQueries: [], + * updateQueries: { + * EmbedQuery: (previous, data) => { + * return previous; + * }, + * }, + * update: (proxy, result) => { + * }, + * }) + * }, + * queries: { + * EmbedQuery: { + * reducer: (previousResult, action, variables) => { + * return previousResult; + * }, + * }, + * }, + * }); + */ +export function registerConfig(cfg) { + Object.keys(cfg.fragments || []).forEach(key => registerFragment(key, cfg.fragments[key])); + Object.keys(cfg.mutations || []).forEach(key => registerMutationOptions(key, cfg.mutations[key])); + Object.keys(cfg.queries || []).forEach(key => registerQueryOptions(key, cfg.queries[key])); +} + +/** + * Get a list of mutation options. + */ +export function getMutationOptions(key) { + init(); + return mutationOptions[key] || []; +} + +/** + * Get a list of query options. + */ +export function getQueryOptions(key) { + init(); + return queryOptions[key] || []; +} + +/** + * Get a document with a fragment named `key`, which contains + * all fragments registered under this key. + */ +export function getFragmentDocument(key) { + init(); + let documents = fragments[key] ? fragments[key].documents : []; + let fields = fragments[key] ? `...${fragments[key].names.join('\n...')}\n` : ' __typename'; + + const main = ` + fragment ${key} on ${fragments[key].type} { + ${fields} + } + `; + const literals = [main, ...documents.map(() => '\n')]; + return gql.apply(null, [literals, ...documents]); +} + +// The fragments and configs are lazily loaded to allow circular dependencies to work. +// TODO: We might want to change this to an explicit register after we have lazy Queries and Mutations. +let initialized = false; + +function init() { + if (initialized) { return; } + initialized = true; + + // Register fragments from framework. + [globalFragments].forEach(map => + Object.keys(map).forEach(key => registerFragment(key, map[key])) + ); + + // Register configs from plugins. + getGraphQLConfigs().forEach(cfg => registerConfig(cfg)); +} + diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index 5d0906b6e..e19224179 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -25,7 +25,7 @@ class CommentBox extends React.Component { postComment = () => { const { commentPostedHandler, - postItem, + postComment, setCommentCountCache, commentCountCache, isReply, @@ -46,7 +46,7 @@ class CommentBox extends React.Component { // Execute preSubmit Hooks this.state.hooks.preSubmit.forEach(hook => hook()); - postItem(comment, 'comments') + postComment(comment, 'comments') .then(({data}) => { const postedComment = data.createComment.comment; @@ -192,7 +192,7 @@ CommentBox.propTypes = { charCountEnable: PropTypes.bool.isRequired, maxCharCount: PropTypes.number, commentPostedHandler: PropTypes.func, - postItem: PropTypes.func.isRequired, + postComment: PropTypes.func.isRequired, cancelButtonClicked: PropTypes.func, assetId: PropTypes.string.isRequired, parentId: PropTypes.string, diff --git a/client/coral-plugin-replies/ReplyBox.js b/client/coral-plugin-replies/ReplyBox.js index d6e2b5631..97667f932 100644 --- a/client/coral-plugin-replies/ReplyBox.js +++ b/client/coral-plugin-replies/ReplyBox.js @@ -12,7 +12,7 @@ class ReplyBox extends Component { render() { const { styles, - postItem, + postComment, assetId, authorId, addNotification, @@ -32,7 +32,7 @@ class ReplyBox extends Component { addNotification={addNotification} authorId={authorId} assetId={assetId} - postItem={postItem} + postComment={postComment} isReply={true} /> ; } @@ -46,7 +46,7 @@ ReplyBox.propTypes = { parentId: PropTypes.string, addNotification: PropTypes.func.isRequired, authorId: PropTypes.string.isRequired, - postItem: PropTypes.func.isRequired, + postComment: PropTypes.func.isRequired, assetId: PropTypes.string.isRequired }; diff --git a/plugins/coral-plugin-respect/client/containers/RespectButton.js b/plugins/coral-plugin-respect/client/containers/RespectButton.js index 38be8a978..fcb7b2a57 100644 --- a/plugins/coral-plugin-respect/client/containers/RespectButton.js +++ b/plugins/coral-plugin-respect/client/containers/RespectButton.js @@ -1,15 +1,15 @@ -import {compose, gql, graphql} from 'react-apollo'; +import {compose, gql} from 'react-apollo'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import get from 'lodash/get'; -import withFragments from 'coral-framework/hocs/withFragments'; +import {withFragments, withMutation} from 'coral-framework/hocs'; import {showSignInDialog} from 'coral-framework/actions/auth'; import RespectButton from '../components/RespectButton'; const isRespectAction = (a) => a.__typename === 'RespectActionSummary'; const COMMENT_FRAGMENT = gql` - fragment RespectButton_updateFragment on Comment { + fragment CoralRespect_UpdateFragment on Comment { action_summaries { ... on RespectActionSummary { count @@ -21,8 +21,8 @@ const COMMENT_FRAGMENT = gql` } `; -const withDeleteAction = graphql(gql` - mutation deleteAction($id: ID!) { +const withDeleteAction = withMutation(gql` + mutation CoralRespect_DeleteAction($id: ID!) { deleteAction(id:$id) { errors { translation_key @@ -66,8 +66,8 @@ const withDeleteAction = graphql(gql` }), }); -const withPostRespect = graphql(gql` - mutation createRespect($respect: CreateRespectInput!) { +const withPostRespect = withMutation(gql` + mutation CoralRespect_CreateRespect($respect: CreateRespectInput!) { createRespect(respect: $respect) { respect { id @@ -137,14 +137,14 @@ const mapDispatchToProps = dispatch => const enhance = compose( withFragments({ root: gql` - fragment RespectButton_root on RootQuery { + fragment CoralRespect_RespectButton_root on RootQuery { me { status } } `, comment: gql` - fragment RespectButton_comment on Comment { + fragment CoralRespect_RespectButton_comment on Comment { action_summaries { ... on RespectActionSummary { count From 13e2382cd79b87937660db02c61f2bb66b2faef8 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 6 May 2017 02:33:23 +0700 Subject: [PATCH 03/16] More code comments --- client/coral-framework/helpers/plugins.js | 2 ++ client/coral-framework/hocs/withMutation.js | 4 ++++ client/coral-framework/hocs/withQuery.js | 4 ++++ client/coral-framework/services/registry.js | 5 +++++ 4 files changed, 15 insertions(+) diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index 76995c204..a21eaba4b 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -41,6 +41,8 @@ function getComponentFragments(components) { }, {}); Object.keys(res).forEach(key => { + + // Assemble arguments for `gql` to call it directly without using template literals. res[key].spreads = `...${res[key].spreads.join('\n...')}\n`; const literals = ['', ...res[key].definitions.map(() => '\n')]; res[key].definitions = gql.apply(null, [literals, ...res[key].definitions]); diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 423ab9793..81529faea 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -5,6 +5,10 @@ import {getMutationOptions} from 'coral-framework/services/registry'; import {store} from 'coral-framework/services/store'; import {getDefinitionName} from '../utils'; +/** + * Exports a HOC with the same signature as `graphql`, that will + * apply mutation options registered in the registry. + */ export default (definitions, config) => WrappedComponent => { config = { ...config, diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js index 9677c223e..575a4484f 100644 --- a/client/coral-framework/hocs/withQuery.js +++ b/client/coral-framework/hocs/withQuery.js @@ -2,6 +2,10 @@ import {graphql} from 'react-apollo'; import {getQueryOptions} from 'coral-framework/services/registry'; import {getDefinitionName, separateDataAndRoot} from '../utils'; +/** + * Exports a HOC with the same signature as `graphql`, that will + * apply query options registered in the registry. + */ export default (definitions, config) => WrappedComponent => { config = { ...config, diff --git a/client/coral-framework/services/registry.js b/client/coral-framework/services/registry.js index 2e889de94..d35861663 100644 --- a/client/coral-framework/services/registry.js +++ b/client/coral-framework/services/registry.js @@ -37,6 +37,8 @@ export function registerFragment(key, document) { * Register mutation options. * * Example: + * // state is the current redux state, which is sometimes + * // necessary to fill the optimistic response. * registerMutationOptions('PostComment', ({variables, state}) => ({ * optimisticResponse: { * CreateComment: { @@ -89,6 +91,8 @@ export function registerQueryOptions(key, config) { * }`, * }, * mutations: { + * // state is the current redux state, which is sometimes + * // necessary to fill the optimistic response. * PostComment: ({variables, state}) => ({ * optimisticResponse: { * [...] @@ -143,6 +147,7 @@ export function getFragmentDocument(key) { let documents = fragments[key] ? fragments[key].documents : []; let fields = fragments[key] ? `...${fragments[key].names.join('\n...')}\n` : ' __typename'; + // Assemble arguments for `gql` to call it directly without using template literals. const main = ` fragment ${key} on ${fragments[key].type} { ${fields} From ce38b9da6483a780aa369b59ead6eb69b4d3aef3 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 8 May 2017 19:41:14 +0700 Subject: [PATCH 04/16] Rename argument name --- client/coral-framework/hocs/withMutation.js | 6 +++--- client/coral-framework/hocs/withQuery.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 81529faea..9c5bad8fd 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -9,14 +9,14 @@ import {getDefinitionName} from '../utils'; * Exports a HOC with the same signature as `graphql`, that will * apply mutation options registered in the registry. */ -export default (definitions, config) => WrappedComponent => { +export default (document, config) => WrappedComponent => { config = { ...config, options: config.options || {}, props: config.props || (data => ({mutate: data.mutate()})), }; const wrappedProps = (data) => { - const name = getDefinitionName(definitions); + const name = getDefinitionName(document); const callbacks = getMutationOptions(name); const mutate = (base) => { const variables = base.variables || config.options.variables; @@ -73,6 +73,6 @@ export default (definitions, config) => WrappedComponent => { }; return config.props({...data, mutate}); }; - const wrapped = graphql(definitions, {...config, props: wrappedProps})(WrappedComponent); + const wrapped = graphql(document, {...config, props: wrappedProps})(WrappedComponent); return wrapped; }; diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js index 575a4484f..42dfe7b35 100644 --- a/client/coral-framework/hocs/withQuery.js +++ b/client/coral-framework/hocs/withQuery.js @@ -6,7 +6,7 @@ import {getDefinitionName, separateDataAndRoot} from '../utils'; * Exports a HOC with the same signature as `graphql`, that will * apply query options registered in the registry. */ -export default (definitions, config) => WrappedComponent => { +export default (document, config) => WrappedComponent => { config = { ...config, options: config.options || {}, @@ -15,7 +15,7 @@ export default (definitions, config) => WrappedComponent => { const wrappedOptions = (data) => { const base = (typeof config.options === 'function') ? config.options(data) : config.options; - const name = getDefinitionName(definitions); + const name = getDefinitionName(document); const configs = getQueryOptions(name); const reducerCallbacks = [base.reducer || (i => i)] @@ -33,6 +33,6 @@ export default (definitions, config) => WrappedComponent => { }; }; - const wrapped = graphql(definitions, {...config, options: wrappedOptions})(WrappedComponent); + const wrapped = graphql(document, {...config, options: wrappedOptions})(WrappedComponent); return wrapped; }; From 4c17cec4fd43cb83b38bd7ba750316333796a75e Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 8 May 2017 21:30:25 +0700 Subject: [PATCH 05/16] Lazily resolve fragments in queries and mutations --- .../graphql/mutations/index.js | 2 -- client/coral-framework/helpers/plugins.js | 8 ++--- client/coral-framework/hocs/withMutation.js | 19 ++++++++++-- client/coral-framework/hocs/withQuery.js | 17 +++++++++-- client/coral-framework/services/registry.js | 30 ++++++++++++++++--- client/coral-framework/utils/index.js | 10 +++++++ 6 files changed, 69 insertions(+), 17 deletions(-) diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 56f63b372..c9152e904 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -8,7 +8,6 @@ import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; import withMutation from '../../hocs/withMutation'; -import {getFragmentDocument} from '../../services/registry'; export const withPostComment = withMutation( gql` @@ -17,7 +16,6 @@ export const withPostComment = withMutation( ...CreateCommentResponse } } - ${getFragmentDocument('CreateCommentResponse')} `, { props: ({mutate}) => ({ postComment: comment => { diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index a21eaba4b..bcf6e4c50 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -5,8 +5,7 @@ import flattenDeep from 'lodash/flattenDeep'; import uniq from 'lodash/uniq'; import pick from 'lodash/pick'; import plugins from 'pluginsConfig'; -import {gql} from 'react-apollo'; -import {getDefinitionName} from 'coral-framework/utils'; +import {getDefinitionName, mergeDocuments} from 'coral-framework/utils'; export const pluginReducers = merge( ...plugins @@ -44,8 +43,7 @@ function getComponentFragments(components) { // Assemble arguments for `gql` to call it directly without using template literals. res[key].spreads = `...${res[key].spreads.join('\n...')}\n`; - const literals = ['', ...res[key].definitions.map(() => '\n')]; - res[key].definitions = gql.apply(null, [literals, ...res[key].definitions]); + res[key].definitions = mergeDocuments(res[key].definitions); }); return res; @@ -86,7 +84,7 @@ export function getSlotsFragments(slots) { export function getGraphQLConfigs() { return plugins - .map(o => o.module.mutations && pick(o.module, ['mutations', 'queries', 'fragments'])) + .map(o => pick(o.module, ['mutations', 'queries', 'fragments'])) .filter(o => o); } diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 9c5bad8fd..f3f092187 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -1,7 +1,8 @@ +import * as React from 'react'; import {graphql} from 'react-apollo'; import merge from 'lodash/merge'; import uniq from 'lodash/uniq'; -import {getMutationOptions} from 'coral-framework/services/registry'; +import {getMutationOptions, resolveFragments} from 'coral-framework/services/registry'; import {store} from 'coral-framework/services/store'; import {getDefinitionName} from '../utils'; @@ -73,6 +74,18 @@ export default (document, config) => WrappedComponent => { }; return config.props({...data, mutate}); }; - const wrapped = graphql(document, {...config, props: wrappedProps})(WrappedComponent); - return wrapped; + + // Lazily resolve fragments from registry to support circular dependencies. + let memoized = null; + const getWrapped = () => { + if (!memoized) { + memoized = graphql(resolveFragments(document), {...config, props: wrappedProps})(WrappedComponent); + } + return memoized; + }; + + return (props) => { + const Wrapped = getWrapped(); + return ; + }; }; diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js index 42dfe7b35..6a5fa7c14 100644 --- a/client/coral-framework/hocs/withQuery.js +++ b/client/coral-framework/hocs/withQuery.js @@ -1,5 +1,6 @@ +import * as React from 'react'; import {graphql} from 'react-apollo'; -import {getQueryOptions} from 'coral-framework/services/registry'; +import {getQueryOptions, resolveFragments} from 'coral-framework/services/registry'; import {getDefinitionName, separateDataAndRoot} from '../utils'; /** @@ -33,6 +34,16 @@ export default (document, config) => WrappedComponent => { }; }; - const wrapped = graphql(document, {...config, options: wrappedOptions})(WrappedComponent); - return wrapped; + let memoized = null; + const getWrapped = () => { + if (!memoized) { + memoized = graphql(resolveFragments(document), {...config, options: wrappedOptions})(WrappedComponent); + } + return memoized; + }; + + return (props) => { + const Wrapped = getWrapped(); + return ; + }; }; diff --git a/client/coral-framework/services/registry.js b/client/coral-framework/services/registry.js index d35861663..8fe934730 100644 --- a/client/coral-framework/services/registry.js +++ b/client/coral-framework/services/registry.js @@ -1,7 +1,7 @@ -import {gql} from 'react-apollo'; -import {getDefinitionName} from 'coral-framework/utils'; +import {getDefinitionName, mergeDocuments} from 'coral-framework/utils'; import {getGraphQLConfigs} from 'coral-framework/helpers/plugins'; import globalFragments from 'coral-framework/graphql/fragments'; +import uniq from 'lodash/uniq'; const fragments = {}; const mutationOptions = {}; @@ -144,6 +144,11 @@ export function getQueryOptions(key) { */ export function getFragmentDocument(key) { init(); + + if (!(key in fragments)) { + return ''; + } + let documents = fragments[key] ? fragments[key].documents : []; let fields = fragments[key] ? `...${fragments[key].names.join('\n...')}\n` : ' __typename'; @@ -153,8 +158,7 @@ export function getFragmentDocument(key) { ${fields} } `; - const literals = [main, ...documents.map(() => '\n')]; - return gql.apply(null, [literals, ...documents]); + return mergeDocuments([main, ...documents]); } // The fragments and configs are lazily loaded to allow circular dependencies to work. @@ -174,3 +178,21 @@ function init() { getGraphQLConfigs().forEach(cfg => registerConfig(cfg)); } +export function resolveFragments(document) { + if (document.loc.source) { + + // resolve fragments from registry + const matchedSubFragments = document.loc.source.body.match(/\.\.\.(.*)/g) || []; + const subFragments = + uniq(matchedSubFragments.map(f => f.replace('...', ''))) + .map(key => getFragmentDocument(key)) + .filter(i => i); + + if (subFragments.length > 0) { + return mergeDocuments([document, ...subFragments]); + } + } else { + console.warn('Can only resolve fragments from documents definied using the gql tag.'); + } + return document; +} diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js index 598fb59ab..70ce89bf7 100644 --- a/client/coral-framework/utils/index.js +++ b/client/coral-framework/utils/index.js @@ -1,3 +1,5 @@ +import {gql} from 'react-apollo'; + export const getTotalActionCount = (type, comment) => { return comment.action_summaries .filter(s => s.__typename === type) @@ -61,3 +63,11 @@ export function separateDataAndRoot( root, }; } + +export function mergeDocuments(documents) { + const main = typeof documents[0] === 'string' ? documents[0] : documents[0].loc.source.body; + const substitutions = documents.slice(1); + const literals = [main, ...substitutions.map(() => '\n')]; + return gql.apply(null, [literals, ...substitutions]); +} + From 10796ed4b337f1f0754eb0b328d6d7b7f660590f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 8 May 2017 22:35:00 +0700 Subject: [PATCH 06/16] Port PostFlag mutation --- .../src/containers/Stream.js | 4 +-- .../coral-embed-stream/src/graphql/index.js | 13 +++++++-- .../graphql/mutations/index.js | 28 +++++++++++-------- .../graphql/mutations/postFlag.graphql | 10 ------- 4 files changed, 30 insertions(+), 25 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/postFlag.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 08adbd930..880098388 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -6,7 +6,7 @@ import uniqBy from 'lodash/uniqBy'; import sortBy from 'lodash/sortBy'; import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; -import {withPostComment, postFlag, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; +import {withPostComment, withPostFlag, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; import {setCommentCountCache, setActiveReplyBox} from '../actions/stream'; @@ -237,7 +237,7 @@ export default compose( withFragments(fragments), connect(mapStateToProps, mapDispatchToProps), withPostComment, - postFlag, + withPostFlag, postDontAgree, addCommentTag, removeCommentTag, diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 2d6f943a2..5add9eb30 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,8 +3,17 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + CreateFlagResponse: gql` + fragment CoralEmbedStream_CreateFlagResponse on CreateFlagResponse { + flag { + id + } + errors { + translation_key + } + }`, CreateCommentResponse: gql` - fragment Coral_CreateCommentResponse on CreateCommentResponse { + fragment CoralEmbedStream_CreateCommentResponse on CreateCommentResponse { comment { ...Coral_CreateCommentResponse_Comment replies { @@ -16,7 +25,7 @@ const config = { } } - fragment Coral_CreateCommentResponse_Comment on Comment { + fragment CoralEmbedStream_CreateCommentResponse_Comment on Comment { id body created_at diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index dbc8f100e..430145122 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import POST_FLAG from './postFlag.graphql'; import POST_DONT_AGREE from './postDontAgree.graphql'; import DELETE_ACTION from './deleteAction.graphql'; import ADD_COMMENT_TAG from './addCommentTag.graphql'; @@ -27,16 +26,23 @@ export const withPostComment = withMutation( }), }); -export const postFlag = graphql(POST_FLAG, { - props: ({mutate}) => ({ - postFlag: (flag) => { - return mutate({ - variables: { - flag - } - }); - }}), -}); +export const withPostFlag = withMutation( + gql` + mutation CreateFlag($flag: CreateFlagInput!) { + createFlag(flag: $flag) { + ...CreateFlagResponse + } + } + `, { + props: ({mutate}) => ({ + postFlag: (flag) => { + return mutate({ + variables: { + flag + } + }); + }}), + }); export const postDontAgree = graphql(POST_DONT_AGREE, { props: ({mutate}) => ({ diff --git a/client/coral-framework/graphql/mutations/postFlag.graphql b/client/coral-framework/graphql/mutations/postFlag.graphql deleted file mode 100644 index cabc2feef..000000000 --- a/client/coral-framework/graphql/mutations/postFlag.graphql +++ /dev/null @@ -1,10 +0,0 @@ -mutation CreateFlag($flag: CreateFlagInput!) { - createFlag(flag:$flag) { - flag { - id - } - errors { - translation_key - } - } -} From 83ef41000c1b6581280600c90b50103317c3d11f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 8 May 2017 22:56:01 +0700 Subject: [PATCH 07/16] Port PostDontAgree mutation --- .../src/containers/Stream.js | 4 +-- .../coral-embed-stream/src/graphql/index.js | 16 ++++++++-- .../graphql/mutations/index.js | 30 +++++++++++-------- .../graphql/mutations/postDontAgree.graphql | 10 ------- 4 files changed, 34 insertions(+), 26 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/postDontAgree.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 880098388..9b01ee439 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -6,7 +6,7 @@ import uniqBy from 'lodash/uniqBy'; import sortBy from 'lodash/sortBy'; import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; -import {withPostComment, withPostFlag, postDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; +import {withPostComment, withPostFlag, withPostDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; import {setCommentCountCache, setActiveReplyBox} from '../actions/stream'; @@ -238,7 +238,7 @@ export default compose( connect(mapStateToProps, mapDispatchToProps), withPostComment, withPostFlag, - postDontAgree, + withPostDontAgree, addCommentTag, removeCommentTag, ignoreUser, diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 5add9eb30..e71eef1cb 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -11,7 +11,18 @@ const config = { errors { translation_key } - }`, + } + `, + CreateDontAgreeResponse : gql` + fragment CoralEmbedStream_CreateDontAgreeResponse on CreateDontAgreeResponse { + dontagree { + id + } + errors { + translation_key + } + } + `, CreateCommentResponse: gql` fragment CoralEmbedStream_CreateCommentResponse on CreateCommentResponse { comment { @@ -45,7 +56,8 @@ const config = { created_at } } - }`, + } + `, }, mutations: { PostComment: ({ diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 430145122..e758d2dfa 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import POST_DONT_AGREE from './postDontAgree.graphql'; import DELETE_ACTION from './deleteAction.graphql'; import ADD_COMMENT_TAG from './addCommentTag.graphql'; import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; @@ -28,7 +27,7 @@ export const withPostComment = withMutation( export const withPostFlag = withMutation( gql` - mutation CreateFlag($flag: CreateFlagInput!) { + mutation PostFlag($flag: CreateFlagInput!) { createFlag(flag: $flag) { ...CreateFlagResponse } @@ -44,16 +43,23 @@ export const withPostFlag = withMutation( }}), }); -export const postDontAgree = graphql(POST_DONT_AGREE, { - props: ({mutate}) => ({ - postDontAgree: (dontagree) => { - return mutate({ - variables: { - dontagree - } - }); - }}), -}); +export const withPostDontAgree = withMutation( + gql` + mutation CreateDontAgree($dontagree: CreateDontAgreeInput!) { + createDontAgree(dontagree: $dontagree) { + ...CreateDontAgreeResponse + } + } + `, { + props: ({mutate}) => ({ + postDontAgree: (dontagree) => { + return mutate({ + variables: { + dontagree + } + }); + }}), + }); export const deleteAction = graphql(DELETE_ACTION, { props: ({mutate}) => ({ diff --git a/client/coral-framework/graphql/mutations/postDontAgree.graphql b/client/coral-framework/graphql/mutations/postDontAgree.graphql deleted file mode 100644 index 6e36d48b8..000000000 --- a/client/coral-framework/graphql/mutations/postDontAgree.graphql +++ /dev/null @@ -1,10 +0,0 @@ -mutation CreateDontAgree($dontagree: CreateDontAgreeInput!) { - createDontAgree(dontagree:$dontagree) { - dontagree { - id - } - errors { - translation_key - } - } -} From 0e931d023dc93989170eb9b852b9bea7a1af1184 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 01:15:07 +0700 Subject: [PATCH 08/16] Port DeleteAction Mutation --- .../src/containers/Stream.js | 7 +++-- .../coral-embed-stream/src/graphql/index.js | 7 +++++ .../graphql/mutations/deleteAction.graphql | 7 ----- .../graphql/mutations/index.js | 28 +++++++++++-------- 4 files changed, 29 insertions(+), 20 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/deleteAction.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 9b01ee439..913d894f3 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -6,7 +6,10 @@ import uniqBy from 'lodash/uniqBy'; import sortBy from 'lodash/sortBy'; import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; -import {withPostComment, withPostFlag, withPostDontAgree, deleteAction, addCommentTag, removeCommentTag, ignoreUser} from 'coral-framework/graphql/mutations'; +import { + withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, + addCommentTag, removeCommentTag, ignoreUser, +} from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; import {setCommentCountCache, setActiveReplyBox} from '../actions/stream'; @@ -242,6 +245,6 @@ export default compose( addCommentTag, removeCommentTag, ignoreUser, - deleteAction, + withDeleteAction, )(StreamContainer); diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index e71eef1cb..001c953f9 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,6 +3,13 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + DeleteActionResponse: gql` + fragment CoralEmbedStream_DeleteActionResponse on DeleteActionResponse { + errors { + translation_key + } + } + `, CreateFlagResponse: gql` fragment CoralEmbedStream_CreateFlagResponse on CreateFlagResponse { flag { diff --git a/client/coral-framework/graphql/mutations/deleteAction.graphql b/client/coral-framework/graphql/mutations/deleteAction.graphql deleted file mode 100644 index f8adf371c..000000000 --- a/client/coral-framework/graphql/mutations/deleteAction.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation deleteAction ($id: ID!) { - deleteAction(id:$id) { - errors { - translation_key - } - } -} diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index e758d2dfa..1d8e3a081 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import DELETE_ACTION from './deleteAction.graphql'; import ADD_COMMENT_TAG from './addCommentTag.graphql'; import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; @@ -61,16 +60,23 @@ export const withPostDontAgree = withMutation( }}), }); -export const deleteAction = graphql(DELETE_ACTION, { - props: ({mutate}) => ({ - deleteAction: (id) => { - return mutate({ - variables: { - id - } - }); - }}), -}); +export const withDeleteAction = withMutation( + gql` + mutation DeleteAction($id: ID!) { + deleteAction(id:$id) { + ...DeleteActionResponse + } + } + `, { + props: ({mutate}) => ({ + deleteAction: (id) => { + return mutate({ + variables: { + id + } + }); + }}), + }); export const addCommentTag = graphql(ADD_COMMENT_TAG, { props: ({mutate}) => ({ From b87c36078ae7e077bab4a65c8c08e57672cfdae7 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:19:55 +0700 Subject: [PATCH 09/16] Port AddCommentTag Mutation --- .../src/containers/Stream.js | 4 +-- .../coral-embed-stream/src/graphql/index.js | 13 ++++++++ .../graphql/mutations/addCommentTag.graphql | 13 -------- .../graphql/mutations/index.js | 30 +++++++++++-------- 4 files changed, 33 insertions(+), 27 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/addCommentTag.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 913d894f3..4154a3a9a 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -8,7 +8,7 @@ import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; import { withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, - addCommentTag, removeCommentTag, ignoreUser, + withAddCommentTag, removeCommentTag, ignoreUser, } from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; @@ -242,7 +242,7 @@ export default compose( withPostComment, withPostFlag, withPostDontAgree, - addCommentTag, + withAddCommentTag, removeCommentTag, ignoreUser, withDeleteAction, diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 001c953f9..e4db858b9 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,6 +3,19 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + AddCommentTagResponse: gql` + fragment CoralEmbedStream_AddCommentTagResponse on AddCommentTagResponse { + comment { + id + tags { + name + } + } + errors { + translation_key + } + } + `, DeleteActionResponse: gql` fragment CoralEmbedStream_DeleteActionResponse on DeleteActionResponse { errors { diff --git a/client/coral-framework/graphql/mutations/addCommentTag.graphql b/client/coral-framework/graphql/mutations/addCommentTag.graphql deleted file mode 100644 index 5fd63868e..000000000 --- a/client/coral-framework/graphql/mutations/addCommentTag.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation AddCommentTag ($id: ID!, $tag: String!) { - addCommentTag(id:$id, tag:$tag) { - comment { - id - tags { - name - } - } - errors { - translation_key - } - } -} diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 1d8e3a081..602d00cc1 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import ADD_COMMENT_TAG from './addCommentTag.graphql'; import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; @@ -78,17 +77,24 @@ export const withDeleteAction = withMutation( }}), }); -export const addCommentTag = graphql(ADD_COMMENT_TAG, { - props: ({mutate}) => ({ - addCommentTag: ({id, tag}) => { - return mutate({ - variables: { - id, - tag - } - }); - }}), -}); +export const withAddCommentTag = withMutation( + gql` + mutation AddCommentTag($id: ID!, $tag: String!) { + addCommentTag(id:$id, tag:$tag) { + ...AddCommentTagResponse + } + } + `, { + props: ({mutate}) => ({ + addCommentTag: ({id, tag}) => { + return mutate({ + variables: { + id, + tag + } + }); + }}), + }); export const removeCommentTag = graphql(REMOVE_COMMENT_TAG, { props: ({mutate}) => ({ From 774a6f68ac38105cc4f832caf0b7b6c8bc7ee635 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:22:44 +0700 Subject: [PATCH 10/16] Port RemoveCommentTag Mutation --- .../src/containers/Stream.js | 4 +-- .../coral-embed-stream/src/graphql/index.js | 13 ++++++++ .../graphql/mutations/index.js | 30 +++++++++++-------- .../mutations/removeCommentTag.graphql | 13 -------- 4 files changed, 33 insertions(+), 27 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/removeCommentTag.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 4154a3a9a..08f3a1e1b 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -8,7 +8,7 @@ import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; import { withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, - withAddCommentTag, removeCommentTag, ignoreUser, + withAddCommentTag, withRemoveCommentTag, ignoreUser, } from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; @@ -243,7 +243,7 @@ export default compose( withPostFlag, withPostDontAgree, withAddCommentTag, - removeCommentTag, + withRemoveCommentTag, ignoreUser, withDeleteAction, )(StreamContainer); diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index e4db858b9..5e4943b1a 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,6 +3,19 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + RemoveCommentTagResponse: gql` + fragment CoralEmbedStream_RemoveCommentTagResponse on RemoveCommentTagResponse { + comment { + id + tags { + name + } + } + errors { + translation_key + } + } + `, AddCommentTagResponse: gql` fragment CoralEmbedStream_AddCommentTagResponse on AddCommentTagResponse { comment { diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 602d00cc1..6407cf4a0 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import REMOVE_COMMENT_TAG from './removeCommentTag.graphql'; import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; import withMutation from '../../hocs/withMutation'; @@ -96,17 +95,24 @@ export const withAddCommentTag = withMutation( }}), }); -export const removeCommentTag = graphql(REMOVE_COMMENT_TAG, { - props: ({mutate}) => ({ - removeCommentTag: ({id, tag}) => { - return mutate({ - variables: { - id, - tag - } - }); - }}), -}); +export const withRemoveCommentTag = withMutation( + gql` + mutation RemoveCommentTag($id: ID!, $tag: String!) { + removeCommentTag(id:$id, tag:$tag) { + ...RemoveCommentTagResponse + } + } + `, { + props: ({mutate}) => ({ + removeCommentTag: ({id, tag}) => { + return mutate({ + variables: { + id, + tag + } + }); + }}), + }); // TODO: don't rely on refetching. export const ignoreUser = graphql(IGNORE_USER, { diff --git a/client/coral-framework/graphql/mutations/removeCommentTag.graphql b/client/coral-framework/graphql/mutations/removeCommentTag.graphql deleted file mode 100644 index 3826b0703..000000000 --- a/client/coral-framework/graphql/mutations/removeCommentTag.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation RemoveCommentTag ($id: ID!, $tag: String!) { - removeCommentTag(id:$id, tag:$tag) { - comment { - id - tags { - name - } - } - errors { - translation_key - } - } -} From 7077300dbc6b3fbf571f8b3e1ec2dcdffb92fa38 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:45:28 +0700 Subject: [PATCH 11/16] Fix refetchQueries --- client/coral-framework/hocs/withMutation.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index f3f092187..844d4ef49 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -2,6 +2,7 @@ import * as React from 'react'; import {graphql} from 'react-apollo'; import merge from 'lodash/merge'; import uniq from 'lodash/uniq'; +import flatten from 'lodash/flatten'; import {getMutationOptions, resolveFragments} from 'coral-framework/services/registry'; import {store} from 'coral-framework/services/store'; import {getDefinitionName} from '../utils'; @@ -28,10 +29,10 @@ export default (document, config) => WrappedComponent => { ...configs.map(cfg => cfg.optimisticResponse), ); - const refetchQueries = uniq( + const refetchQueries = flatten(uniq([ base.refetchQueries || config.options.refetchQueries, ...configs.map(cfg => cfg.refetchQueries), - ); + ].filter(i => i))); const updateCallbacks = [base.update || config.options.update] From 5c44922d2b5d6274c5f02616b13433c067350e9a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:46:42 +0700 Subject: [PATCH 12/16] Port IgnoreUser mutation --- .../src/containers/Stream.js | 4 +-- .../coral-embed-stream/src/graphql/index.js | 14 ++++++++ .../graphql/mutations/ignoreUser.graphql | 7 ---- .../graphql/mutations/index.js | 32 ++++++++++--------- 4 files changed, 33 insertions(+), 24 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/ignoreUser.graphql diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 08f3a1e1b..47e08fef0 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -8,7 +8,7 @@ import isNil from 'lodash/isNil'; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from '../constants/stream'; import { withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, - withAddCommentTag, withRemoveCommentTag, ignoreUser, + withAddCommentTag, withRemoveCommentTag, withIgnoreUser, } from 'coral-framework/graphql/mutations'; import {notificationActions, authActions} from 'coral-framework'; import {editName} from 'coral-framework/actions/user'; @@ -244,7 +244,7 @@ export default compose( withPostDontAgree, withAddCommentTag, withRemoveCommentTag, - ignoreUser, + withIgnoreUser, withDeleteAction, )(StreamContainer); diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 5e4943b1a..71d1f0924 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,6 +3,13 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + IgnoreUserResponse: gql` + fragment CoralEmbedStream_IgnoreUserResponse on IgnoreUserResponse { + errors { + translation_key + } + } + `, RemoveCommentTagResponse: gql` fragment CoralEmbedStream_RemoveCommentTagResponse on RemoveCommentTagResponse { comment { @@ -93,6 +100,13 @@ const config = { `, }, mutations: { + IgnoreUser: () => ({ + + // TODO: don't rely on refetching. + refetchQueries: [ + 'EmbedQuery', 'myIgnoredUsers', + ], + }), PostComment: ({ variables: {comment: {asset_id, body, parent_id, tags = []}}, state: {auth}, diff --git a/client/coral-framework/graphql/mutations/ignoreUser.graphql b/client/coral-framework/graphql/mutations/ignoreUser.graphql deleted file mode 100644 index ad3c399f3..000000000 --- a/client/coral-framework/graphql/mutations/ignoreUser.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation ignoreUser ($id: ID!) { - ignoreUser(id:$id) { - errors { - translation_key - } - } -} diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 6407cf4a0..0c4a05f12 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ import {graphql, gql} from 'react-apollo'; -import IGNORE_USER from './ignoreUser.graphql'; import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; import withMutation from '../../hocs/withMutation'; @@ -114,20 +113,23 @@ export const withRemoveCommentTag = withMutation( }}), }); -// TODO: don't rely on refetching. -export const ignoreUser = graphql(IGNORE_USER, { - props: ({mutate}) => ({ - ignoreUser: ({id}) => { - return mutate({ - variables: { - id, - }, - refetchQueries: [ - 'EmbedQuery', 'myIgnoredUsers', - ] - }); - }}), -}); +export const withIgnoreUser = withMutation( + gql` + mutation IgnoreUser($id: ID!) { + ignoreUser(id:$id) { + ...IgnoreUserResponse + } + } + `, { + props: ({mutate}) => ({ + ignoreUser: ({id}) => { + return mutate({ + variables: { + id, + }, + }); + }}), + }); // TODO: don't rely on refetching. export const stopIgnoringUser = graphql(STOP_IGNORING_USER, { From 80d08bbe2e1787787a4cab77f71d3923939f43d1 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:50:52 +0700 Subject: [PATCH 13/16] Port StopIgnoringUser Mutation --- .../coral-embed-stream/src/graphql/index.js | 14 ++++++++++ .../graphql/mutations/index.js | 26 +++++++++---------- .../mutations/stopIgnoringUser.graphql | 7 ----- .../containers/ProfileContainer.js | 4 +-- 4 files changed, 29 insertions(+), 22 deletions(-) delete mode 100644 client/coral-framework/graphql/mutations/stopIgnoringUser.graphql diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 71d1f0924..66705c25a 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -3,6 +3,13 @@ import {registerConfig} from 'coral-framework/services/registry'; const config = { fragments: { + StopIgnoringUserResponse: gql` + fragment CoralEmbedStream_StopIgnoringUserResponse on StopIgnoringUserResponse { + errors { + translation_key + } + } + `, IgnoreUserResponse: gql` fragment CoralEmbedStream_IgnoreUserResponse on IgnoreUserResponse { errors { @@ -107,6 +114,13 @@ const config = { 'EmbedQuery', 'myIgnoredUsers', ], }), + StopIgnoringUser: () => ({ + + // TODO: don't rely on refetching. + refetchQueries: [ + 'EmbedQuery', 'myIgnoredUsers', + ], + }), PostComment: ({ variables: {comment: {asset_id, body, parent_id, tags = []}}, state: {auth}, diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 0c4a05f12..f43c68dc0 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,5 +1,4 @@ -import {graphql, gql} from 'react-apollo'; -import STOP_IGNORING_USER from './stopIgnoringUser.graphql'; +import {gql} from 'react-apollo'; import withMutation from '../../hocs/withMutation'; export const withPostComment = withMutation( @@ -131,20 +130,21 @@ export const withIgnoreUser = withMutation( }}), }); -// TODO: don't rely on refetching. -export const stopIgnoringUser = graphql(STOP_IGNORING_USER, { - props: ({mutate}) => { - return { +export const withStopIgnoringUser = withMutation( + gql` + mutation StopIgnoringUser($id: ID!) { + stopIgnoringUser(id:$id) { + ...StopIgnoringUserResponse + } + } + `, { + props: ({mutate}) => ({ stopIgnoringUser: ({id}) => { return mutate({ variables: { id, }, - refetchQueries: [ - 'EmbedQuery', 'myIgnoredUsers', - ] }); - } - }; - } -}); + }}), + }); + diff --git a/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql b/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql deleted file mode 100644 index 042452ff5..000000000 --- a/client/coral-framework/graphql/mutations/stopIgnoringUser.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation stopIgnoringUser ($id: ID!) { - stopIgnoringUser(id:$id) { - errors { - translation_key - } - } -} diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index 39e615725..e289ff485 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -5,7 +5,7 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import {bindActionCreators} from 'redux'; import {myCommentHistory, myIgnoredUsers} from 'coral-framework/graphql/queries'; -import {stopIgnoringUser} from 'coral-framework/graphql/mutations'; +import {withStopIgnoringUser} from 'coral-framework/graphql/mutations'; import {link} from 'coral-framework/services/PymConnection'; import NotLoggedIn from '../components/NotLoggedIn'; @@ -101,5 +101,5 @@ export default compose( connect(mapStateToProps, mapDispatchToProps), myCommentHistory, myIgnoredUsers, - stopIgnoringUser, + withStopIgnoringUser, )(ProfileContainer); From 57e7396997bf9dcb0aa4ff9cfb0a04a172473937 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 02:58:50 +0700 Subject: [PATCH 14/16] Use correct fragment name --- client/coral-embed-stream/src/graphql/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 66705c25a..c9e336941 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -73,9 +73,9 @@ const config = { CreateCommentResponse: gql` fragment CoralEmbedStream_CreateCommentResponse on CreateCommentResponse { comment { - ...Coral_CreateCommentResponse_Comment + ...CoralEmbedStream_CreateCommentResponse_Comment replies { - ...Coral_CreateCommentResponse_Comment + ...CoralEmbedStream_CreateCommentResponse_Comment } } errors { From 028e13761f616cfe455a83c9710ed71e5b4fdee0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 9 May 2017 03:05:19 +0700 Subject: [PATCH 15/16] Complete graphql code refactor in framework --- .../{fragments/index.js => fragments.js} | 0 .../fragments/actionSummaryView.graphql | 8 ---- .../graphql/fragments/commentView.graphql | 18 --------- .../{mutations/index.js => mutations.js} | 2 +- .../coral-framework/graphql/queries/index.js | 13 ------- .../graphql/queries/myCommentHistory.graphql | 14 ------- .../graphql/queries/myIgnoredUsers.graphql | 6 --- .../containers/ProfileContainer.js | 39 +++++++++++++++++-- 8 files changed, 36 insertions(+), 64 deletions(-) rename client/coral-framework/graphql/{fragments/index.js => fragments.js} (100%) delete mode 100644 client/coral-framework/graphql/fragments/actionSummaryView.graphql delete mode 100644 client/coral-framework/graphql/fragments/commentView.graphql rename client/coral-framework/graphql/{mutations/index.js => mutations.js} (98%) delete mode 100644 client/coral-framework/graphql/queries/index.js delete mode 100644 client/coral-framework/graphql/queries/myCommentHistory.graphql delete mode 100644 client/coral-framework/graphql/queries/myIgnoredUsers.graphql diff --git a/client/coral-framework/graphql/fragments/index.js b/client/coral-framework/graphql/fragments.js similarity index 100% rename from client/coral-framework/graphql/fragments/index.js rename to client/coral-framework/graphql/fragments.js diff --git a/client/coral-framework/graphql/fragments/actionSummaryView.graphql b/client/coral-framework/graphql/fragments/actionSummaryView.graphql deleted file mode 100644 index 4ac232bf6..000000000 --- a/client/coral-framework/graphql/fragments/actionSummaryView.graphql +++ /dev/null @@ -1,8 +0,0 @@ -fragment actionSummaryView on ActionSummary { - __typename - count - current_user { - id - created_at - } -} diff --git a/client/coral-framework/graphql/fragments/commentView.graphql b/client/coral-framework/graphql/fragments/commentView.graphql deleted file mode 100644 index 0ed5e00b8..000000000 --- a/client/coral-framework/graphql/fragments/commentView.graphql +++ /dev/null @@ -1,18 +0,0 @@ -#import "../fragments/actionSummaryView.graphql" - -fragment commentView on Comment { - id - body - created_at - status - tags { - name - } - user { - id - name: username - } - action_summaries { - ...actionSummaryView - } -} diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations.js similarity index 98% rename from client/coral-framework/graphql/mutations/index.js rename to client/coral-framework/graphql/mutations.js index f43c68dc0..1f1179b58 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations.js @@ -1,5 +1,5 @@ import {gql} from 'react-apollo'; -import withMutation from '../../hocs/withMutation'; +import withMutation from '../hocs/withMutation'; export const withPostComment = withMutation( gql` diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js deleted file mode 100644 index d76614a68..000000000 --- a/client/coral-framework/graphql/queries/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import {graphql} from 'react-apollo'; -import MY_COMMENT_HISTORY from './myCommentHistory.graphql'; -import MY_IGNORED_USERS from './myIgnoredUsers.graphql'; - -export const myCommentHistory = graphql(MY_COMMENT_HISTORY, {}); - -export const myIgnoredUsers = graphql(MY_IGNORED_USERS, { - props: ({data}) => { - return ({ - myIgnoredUsersData: data - }); - } -}); diff --git a/client/coral-framework/graphql/queries/myCommentHistory.graphql b/client/coral-framework/graphql/queries/myCommentHistory.graphql deleted file mode 100644 index 0b37b192a..000000000 --- a/client/coral-framework/graphql/queries/myCommentHistory.graphql +++ /dev/null @@ -1,14 +0,0 @@ -query myCommentHistory { - me { - comments { - id - body - asset { - id - title - url - } - created_at - } - } -} diff --git a/client/coral-framework/graphql/queries/myIgnoredUsers.graphql b/client/coral-framework/graphql/queries/myIgnoredUsers.graphql deleted file mode 100644 index d81531e37..000000000 --- a/client/coral-framework/graphql/queries/myIgnoredUsers.graphql +++ /dev/null @@ -1,6 +0,0 @@ -query myIgnoredUsers { - myIgnoredUsers { - id, - username, - } -} diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index e289ff485..f9c8d892f 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -1,10 +1,9 @@ import {connect} from 'react-redux'; -import {compose} from 'react-apollo'; +import {compose, graphql, gql} from 'react-apollo'; import React, {Component} from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; import {bindActionCreators} from 'redux'; -import {myCommentHistory, myIgnoredUsers} from 'coral-framework/graphql/queries'; import {withStopIgnoringUser} from 'coral-framework/graphql/mutations'; import {link} from 'coral-framework/services/PymConnection'; @@ -88,6 +87,38 @@ class ProfileContainer extends Component { } } +// TODO: These currently relies on refetching (see ignoreUser and stopIgnoringUser mutations). +// +const withMyIgnoredUsersQuery = graphql(gql` + query myIgnoredUsers { + myIgnoredUsers { + id, + username, + } + }`, { + props: ({data}) => { + return ({ + myIgnoredUsersData: data + }); + } + }); + +const withMyCommentHistoryQuery = graphql(gql` + query myCommentHistory { + me { + comments { + id + body + asset { + id + title + url + } + created_at + } + } + }`); + const mapStateToProps = state => ({ user: state.user.toJS(), asset: state.asset.toJS(), @@ -99,7 +130,7 @@ const mapDispatchToProps = dispatch => export default compose( connect(mapStateToProps, mapDispatchToProps), - myCommentHistory, - myIgnoredUsers, + withMyCommentHistoryQuery, + withMyIgnoredUsersQuery, withStopIgnoringUser, )(ProfileContainer); From 95a7a1ca47a1f4e103cd9522b3f1478c92a9d5da Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 12 May 2017 20:15:04 +0700 Subject: [PATCH 16/16] Rename registry --- .../coral-embed-stream/src/graphql/index.js | 6 +-- client/coral-framework/helpers/plugins.js | 2 +- client/coral-framework/hocs/withMutation.js | 6 +-- client/coral-framework/hocs/withQuery.js | 4 +- .../{registry.js => graphqlRegistry.js} | 44 +++++++++---------- 5 files changed, 31 insertions(+), 31 deletions(-) rename client/coral-framework/services/{registry.js => graphqlRegistry.js} (76%) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index a123de7e7..daa8389b3 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -1,8 +1,8 @@ import {gql} from 'react-apollo'; -import {registerConfig} from 'coral-framework/services/registry'; +import {add} from 'coral-framework/services/graphqlRegistry'; import update from 'immutability-helper'; -const config = { +const extension = { fragments: { EditCommentResponse: gql` fragment CoralEmbedStream_EditCommentResponse on EditCommentResponse { @@ -267,4 +267,4 @@ const config = { }, }; -registerConfig(config); +add(extension); diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index e55715e4b..bde7f9d68 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -82,7 +82,7 @@ export function getSlotsFragments(slots) { }; } -export function getGraphQLConfigs() { +export function getGraphQLExtensions() { return plugins .map(o => pick(o.module, ['mutations', 'queries', 'fragments'])) .filter(o => o); diff --git a/client/coral-framework/hocs/withMutation.js b/client/coral-framework/hocs/withMutation.js index 59a4fb627..c08a53614 100644 --- a/client/coral-framework/hocs/withMutation.js +++ b/client/coral-framework/hocs/withMutation.js @@ -4,13 +4,13 @@ import merge from 'lodash/merge'; import uniq from 'lodash/uniq'; import flatten from 'lodash/flatten'; import isEmpty from 'lodash/isEmpty'; -import {getMutationOptions, resolveFragments} from 'coral-framework/services/registry'; +import {getMutationOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry'; import {store} from 'coral-framework/services/store'; import {getDefinitionName} from '../utils'; /** * Exports a HOC with the same signature as `graphql`, that will - * apply mutation options registered in the registry. + * apply mutation options registered in the graphRegistry. */ export default (document, config) => WrappedComponent => { config = { @@ -80,7 +80,7 @@ export default (document, config) => WrappedComponent => { return config.props({...data, mutate}); }; - // Lazily resolve fragments from registry to support circular dependencies. + // Lazily resolve fragments from graphRegistry to support circular dependencies. let memoized = null; const getWrapped = () => { if (!memoized) { diff --git a/client/coral-framework/hocs/withQuery.js b/client/coral-framework/hocs/withQuery.js index 6a5fa7c14..a29d25241 100644 --- a/client/coral-framework/hocs/withQuery.js +++ b/client/coral-framework/hocs/withQuery.js @@ -1,11 +1,11 @@ import * as React from 'react'; import {graphql} from 'react-apollo'; -import {getQueryOptions, resolveFragments} from 'coral-framework/services/registry'; +import {getQueryOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry'; import {getDefinitionName, separateDataAndRoot} from '../utils'; /** * Exports a HOC with the same signature as `graphql`, that will - * apply query options registered in the registry. + * apply query options registered in the graphRegistry. */ export default (document, config) => WrappedComponent => { config = { diff --git a/client/coral-framework/services/registry.js b/client/coral-framework/services/graphqlRegistry.js similarity index 76% rename from client/coral-framework/services/registry.js rename to client/coral-framework/services/graphqlRegistry.js index 8fe934730..78d27d038 100644 --- a/client/coral-framework/services/registry.js +++ b/client/coral-framework/services/graphqlRegistry.js @@ -1,5 +1,5 @@ import {getDefinitionName, mergeDocuments} from 'coral-framework/utils'; -import {getGraphQLConfigs} from 'coral-framework/helpers/plugins'; +import {getGraphQLExtensions} from 'coral-framework/helpers/plugins'; import globalFragments from 'coral-framework/graphql/fragments'; import uniq from 'lodash/uniq'; @@ -10,16 +10,16 @@ const queryOptions = {}; const getTypeName = (ast) => ast.definitions[0].typeCondition.name.value; /** - * Register fragment + * Add fragment * * Example: - * registerFragment('MyFragment', gql` + * addFragment('MyFragment', gql` * fragment Plugin_MyFragment on Comment { * body * } * `); */ -export function registerFragment(key, document) { +export function addFragment(key, document) { const type = getTypeName(document); const name = getDefinitionName(document); if (!(key in fragments)) { @@ -34,12 +34,12 @@ export function registerFragment(key, document) { } /** - * Register mutation options. + * Add mutation options. * * Example: * // state is the current redux state, which is sometimes * // necessary to fill the optimistic response. - * registerMutationOptions('PostComment', ({variables, state}) => ({ + * addMutationOptions('PostComment', ({variables, state}) => ({ * optimisticResponse: { * CreateComment: { * extra: '', @@ -55,7 +55,7 @@ export function registerFragment(key, document) { * }, * }) */ -export function registerMutationOptions(key, config) { +export function addMutationOptions(key, config) { if (!(key in mutationOptions)) { mutationOptions[key] = [config]; } else { @@ -64,14 +64,14 @@ export function registerMutationOptions(key, config) { } /** - * Register query options. + * Add query options. * * Example: - * registerQueryOptions('EmbedQuery', { + * addQueryOptions('EmbedQuery', { * reducer: (previousResult, action, variables) => previousResult, * }); */ -export function registerQueryOptions(key, config) { +export function addQueryOptions(key, config) { if (!(key in queryOptions)) { queryOptions[key] = [config]; } else { @@ -80,10 +80,10 @@ export function registerQueryOptions(key, config) { } /** - * Register all fragments, mutation options, and query options defined in the object. + * Add all fragments, mutation options, and query options defined in the object. * * Example: - * registerConfig({ + * add({ * fragments: { * CreateCommentResponse: gql` * fragment CoralRandomEmoji_CreateCommentResponse on CreateCommentResponse { @@ -116,10 +116,10 @@ export function registerQueryOptions(key, config) { * }, * }); */ -export function registerConfig(cfg) { - Object.keys(cfg.fragments || []).forEach(key => registerFragment(key, cfg.fragments[key])); - Object.keys(cfg.mutations || []).forEach(key => registerMutationOptions(key, cfg.mutations[key])); - Object.keys(cfg.queries || []).forEach(key => registerQueryOptions(key, cfg.queries[key])); +export function add(extension) { + Object.keys(extension.fragments || []).forEach(key => addFragment(key, extension.fragments[key])); + Object.keys(extension.mutations || []).forEach(key => addMutationOptions(key, extension.mutations[key])); + Object.keys(extension.queries || []).forEach(key => addQueryOptions(key, extension.queries[key])); } /** @@ -140,7 +140,7 @@ export function getQueryOptions(key) { /** * Get a document with a fragment named `key`, which contains - * all fragments registered under this key. + * all fragments added under this key. */ export function getFragmentDocument(key) { init(); @@ -162,20 +162,20 @@ export function getFragmentDocument(key) { } // The fragments and configs are lazily loaded to allow circular dependencies to work. -// TODO: We might want to change this to an explicit register after we have lazy Queries and Mutations. +// TODO: We might want to change this to an explicit add after we have lazy Queries and Mutations. let initialized = false; function init() { if (initialized) { return; } initialized = true; - // Register fragments from framework. + // Add fragments from framework. [globalFragments].forEach(map => - Object.keys(map).forEach(key => registerFragment(key, map[key])) + Object.keys(map).forEach(key => addFragment(key, map[key])) ); - // Register configs from plugins. - getGraphQLConfigs().forEach(cfg => registerConfig(cfg)); + // Add configs from plugins. + getGraphQLExtensions().forEach(ext => add(ext)); } export function resolveFragments(document) {