mirror of
https://github.com/wassname/talk.git
synced 2026-07-12 06:54:36 +08:00
157 lines
4.6 KiB
JavaScript
157 lines
4.6 KiB
JavaScript
import * as React from 'react';
|
|
import {graphql} from 'react-apollo';
|
|
import {getQueryOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry';
|
|
import {getDefinitionName, separateDataAndRoot, getResponseErrors} from '../utils';
|
|
import PropTypes from 'prop-types';
|
|
|
|
const withSkipOnErrors = (reducer) => (prev, action, ...rest) => {
|
|
if (action.type === 'APOLLO_MUTATION_RESULT' && getResponseErrors(action.result)) {
|
|
return prev;
|
|
}
|
|
return reducer(prev, action, ...rest);
|
|
};
|
|
|
|
function networkStatusToString(networkStatus) {
|
|
switch(networkStatus) {
|
|
case 1:
|
|
return 'loading';
|
|
case 2:
|
|
return 'setVariables';
|
|
case 3:
|
|
return 'fetchMore';
|
|
case 4:
|
|
return 'refetch';
|
|
case 6:
|
|
return 'poll';
|
|
case 7:
|
|
return 'ready';
|
|
case 8:
|
|
return 'error';
|
|
}
|
|
throw new Error(`Unknown network status ${networkStatus}`);
|
|
}
|
|
|
|
/**
|
|
* Exports a HOC with the same signature as `graphql`, that will
|
|
* apply query options registered in the graphRegistry.
|
|
*/
|
|
export default (document, config = {}) => (WrappedComponent) => {
|
|
const name = getDefinitionName(document);
|
|
|
|
return class WithQuery extends React.Component {
|
|
static contextTypes = {
|
|
eventEmitter: PropTypes.object,
|
|
};
|
|
|
|
// Lazily resolve fragments from graphRegistry to support circular dependencies.
|
|
memoized = null;
|
|
lastNetworkStatus = null;
|
|
|
|
emitWhenNeeded(data) {
|
|
const {variables, networkStatus} = data;
|
|
if (this.lastNetworkStatus === networkStatus) {
|
|
return;
|
|
}
|
|
this.lastNetworkStatus = networkStatus;
|
|
|
|
const status = networkStatusToString(networkStatus);
|
|
|
|
const {root} = separateDataAndRoot(data);
|
|
this.context.eventEmitter.emit(`query.${name}.${status}`, {variables, data: root});
|
|
}
|
|
|
|
wrappedConfig = {
|
|
...config,
|
|
options: config.options || {},
|
|
props: (args) => {
|
|
this.emitWhenNeeded(args.data);
|
|
|
|
const wrappedArgs = {
|
|
...args,
|
|
data: {
|
|
...args.data,
|
|
subscribeToMore: (stmArgs) => {
|
|
|
|
// Resolve document fragments before passing it to `apollo-client`.
|
|
return args.data.subscribeToMore({
|
|
...stmArgs,
|
|
document: resolveFragments(stmArgs.document),
|
|
onError: (err) => {
|
|
if (stmArgs.onErr) {
|
|
return stmArgs.onErr(err);
|
|
}
|
|
throw err;
|
|
},
|
|
});
|
|
},
|
|
fetchMore: (lmArgs) => {
|
|
const fetchName = getDefinitionName(lmArgs.query);
|
|
this.context.eventEmitter.emit(
|
|
`query.${name}.fetchMore.${fetchName}.begin`,
|
|
{variables: lmArgs.variables});
|
|
|
|
// Resolve document fragments before passing it to `apollo-client`.
|
|
return args.data.fetchMore({
|
|
...lmArgs,
|
|
query: resolveFragments(lmArgs.query),
|
|
})
|
|
.then((res) => {
|
|
this.context.eventEmitter.emit(
|
|
`query.${name}.fetchMore.${fetchName}.success`,
|
|
{variables: lmArgs.variables, data: res.data});
|
|
return Promise.resolve(res);
|
|
})
|
|
.catch((err) => {
|
|
this.context.eventEmitter.emit(
|
|
`query.${name}.fetchMore.${fetchName}.error`,
|
|
{variables: lmArgs.variables, error: err});
|
|
throw err;
|
|
});
|
|
},
|
|
},
|
|
};
|
|
return config.props
|
|
? config.props(wrappedArgs)
|
|
: separateDataAndRoot(wrappedArgs.data);
|
|
},
|
|
};
|
|
|
|
wrappedOptions = (data) => {
|
|
const base = (typeof this.wrappedConfig.options === 'function')
|
|
? this.wrappedConfig.options(data)
|
|
: this.wrappedConfig.options;
|
|
const configs = getQueryOptions(name);
|
|
const reducerCallbacks =
|
|
[base.reducer || ((i) => i)]
|
|
.concat(...configs.map((cfg) => cfg.reducer))
|
|
.filter((i) => i);
|
|
|
|
const reducer = withSkipOnErrors(
|
|
reducerCallbacks.reduce(
|
|
(a, b) => (prev, ...rest) =>
|
|
b(a(prev, ...rest), ...rest),
|
|
));
|
|
|
|
return {
|
|
...base,
|
|
reducer,
|
|
};
|
|
};
|
|
|
|
getWrapped = () => {
|
|
if (!this.memoized) {
|
|
this.memoized = graphql(
|
|
resolveFragments(document),
|
|
{...this.wrappedConfig, options: this.wrappedOptions},
|
|
)(WrappedComponent);
|
|
}
|
|
return this.memoized;
|
|
};
|
|
|
|
render() {
|
|
const Wrapped = this.getWrapped();
|
|
return <Wrapped {...this.props} />;
|
|
}
|
|
};
|
|
};
|