mirror of
https://github.com/wassname/talk.git
synced 2026-08-20 12:50:41 +08:00
Implement extendable GraphQL framework
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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
|
||||
? <CommentBox
|
||||
addNotification={this.props.addNotification}
|
||||
postItem={this.props.postItem}
|
||||
postComment={this.props.postComment}
|
||||
appendItemArray={this.props.appendItemArray}
|
||||
updateItem={this.props.updateItem}
|
||||
setCommentCountCache={this.props.setCommentCountCache}
|
||||
@@ -123,7 +123,7 @@ class Stream extends React.Component {
|
||||
activeReplyBox={this.props.activeReplyBox}
|
||||
addNotification={addNotification}
|
||||
depth={0}
|
||||
postItem={this.props.postItem}
|
||||
postComment={this.props.postComment}
|
||||
asset={asset}
|
||||
currentUser={user}
|
||||
highlighted={comment.id}
|
||||
@@ -162,7 +162,7 @@ class Stream extends React.Component {
|
||||
activeReplyBox={this.props.activeReplyBox}
|
||||
addNotification={addNotification}
|
||||
depth={0}
|
||||
postItem={postItem}
|
||||
postComment={postComment}
|
||||
asset={asset}
|
||||
currentUser={user}
|
||||
postLike={postLike}
|
||||
@@ -199,7 +199,7 @@ class Stream extends React.Component {
|
||||
|
||||
Stream.propTypes = {
|
||||
addNotification: PropTypes.func.isRequired,
|
||||
postItem: PropTypes.func.isRequired,
|
||||
postComment: PropTypes.func.isRequired,
|
||||
|
||||
// dispatch action to add a tag to a comment
|
||||
addCommentTag: PropTypes.func,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {gql} from 'react-apollo';
|
||||
import Comment from '../components/Comment';
|
||||
import withFragments from 'coral-framework/hocs/withFragments';
|
||||
import {withFragments} from 'coral-framework/hocs';
|
||||
import {getSlotsFragments} from 'coral-framework/helpers/plugins';
|
||||
|
||||
const pluginFragments = getSlotsFragments([
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {compose, gql, graphql} from 'react-apollo';
|
||||
import {compose, gql} from 'react-apollo';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
@@ -8,7 +8,8 @@ import renderComponent from 'recompose/renderComponent';
|
||||
|
||||
import {Spinner} from 'coral-ui';
|
||||
import {authActions, assetActions, pym} from 'coral-framework';
|
||||
import {getDefinitionName, separateDataAndRoot} from 'coral-framework/utils';
|
||||
import {getDefinitionName} from 'coral-framework/utils';
|
||||
import {withQuery} from 'coral-framework/hocs';
|
||||
import Embed from '../components/Embed';
|
||||
import {setCommentCountCache, viewAllComments} from '../actions/stream';
|
||||
import {setActiveTab} from '../actions/embed';
|
||||
@@ -75,7 +76,7 @@ const EMBED_QUERY = gql`
|
||||
${Stream.fragments.root}
|
||||
`;
|
||||
|
||||
export const withQuery = graphql(EMBED_QUERY, {
|
||||
export const withEmbedQuery = withQuery(EMBED_QUERY, {
|
||||
options: ({auth, commentId, assetId, assetUrl}) => ({
|
||||
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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
@@ -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';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// fragments defined here are automatically registered.
|
||||
export default {};
|
||||
@@ -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}) => ({
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#import "../fragments/commentView.graphql"
|
||||
|
||||
mutation CreateComment ($comment: CreateCommentInput!) {
|
||||
createComment(comment: $comment) {
|
||||
comment {
|
||||
...commentView
|
||||
replyCount
|
||||
replies {
|
||||
...commentView
|
||||
}
|
||||
}
|
||||
errors {
|
||||
translation_key
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export {default as withFragments} from './withFragments';
|
||||
export {default as withMutation} from './withMutation';
|
||||
export {default as withQuery} from './withQuery';
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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} />
|
||||
</div>;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user