mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 22:14:11 +08:00
134 lines
4.0 KiB
JavaScript
134 lines
4.0 KiB
JavaScript
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 isEmpty from 'lodash/isEmpty';
|
|
import {getMutationOptions, resolveFragments} from 'coral-framework/services/graphqlRegistry';
|
|
import {getStore} from 'coral-framework/services/store';
|
|
import {getDefinitionName, getResponseErrors} from '../utils';
|
|
import t from 'coral-framework/services/i18n';
|
|
|
|
class ResponseErrors extends Error {
|
|
constructor(errors) {
|
|
super(`Response Errors ${JSON.stringify(errors)}`);
|
|
this.errors = errors.map((e) => new ResponseError(e));
|
|
}
|
|
}
|
|
|
|
class ResponseError {
|
|
constructor(error) {
|
|
Object.assign(this, error);
|
|
}
|
|
|
|
translate(...args) {
|
|
return t(`error.${this.translation_key}`, ...args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exports a HOC with the same signature as `graphql`, that will
|
|
* apply mutation options registered in the graphRegistry.
|
|
*/
|
|
export default (document, config = {}) => (WrappedComponent) => {
|
|
config = {
|
|
...config,
|
|
options: config.options || {},
|
|
props: config.props || ((data) => ({mutate: data.mutate()})),
|
|
};
|
|
const wrappedProps = (data) => {
|
|
const name = getDefinitionName(document);
|
|
const callbacks = getMutationOptions(name);
|
|
const mutate = (base) => {
|
|
const variables = base.variables || config.options.variables;
|
|
const configs = callbacks.map((cb) => cb({variables, state: getStore().getState()}));
|
|
|
|
const optimisticResponse = merge(
|
|
base.optimisticResponse || config.options.optimisticResponse,
|
|
...configs.map((cfg) => cfg.optimisticResponse),
|
|
);
|
|
|
|
const refetchQueries = flatten(uniq([
|
|
base.refetchQueries || config.options.refetchQueries,
|
|
...configs.map((cfg) => cfg.refetchQueries),
|
|
].filter((i) => i)));
|
|
|
|
const updateCallbacks =
|
|
[base.update || config.options.update]
|
|
.concat(...configs.map((cfg) => cfg.update))
|
|
.filter((i) => i);
|
|
|
|
const update = (proxy, result) => {
|
|
if (getResponseErrors(result)) {
|
|
|
|
// Do not run updates when we have mutation errors.
|
|
return;
|
|
}
|
|
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] = (prev, result) => {
|
|
if (getResponseErrors(result.mutationResult)) {
|
|
|
|
// Do not run updates when we have mutation errors.
|
|
return prev;
|
|
}
|
|
return map[key](prev, result);
|
|
};
|
|
} 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,
|
|
};
|
|
if (isEmpty(wrappedConfig.optimisticResponse)) {
|
|
delete wrappedConfig.optimisticResponse;
|
|
}
|
|
return data.mutate(wrappedConfig)
|
|
.then((res) => {
|
|
const errors = getResponseErrors(res);
|
|
if (errors) {
|
|
throw new ResponseErrors(errors);
|
|
}
|
|
return Promise.resolve(res);
|
|
});
|
|
};
|
|
return config.props({...data, mutate});
|
|
};
|
|
|
|
// Lazily resolve fragments from graphRegistry 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 <Wrapped {...props} />;
|
|
};
|
|
};
|