saleor-dashboard/src/queries.tsx
2020-02-20 15:18:22 +01:00

164 lines
4.2 KiB
TypeScript

import { ApolloQueryResult } from "apollo-client";
import { DocumentNode } from "graphql";
import gql from "graphql-tag";
import React from "react";
import { Query, QueryResult } from "react-apollo";
import { useIntl } from "react-intl";
import useAppState from "./hooks/useAppState";
import useNotifier from "./hooks/useNotifier";
import { commonMessages } from "./intl";
import { maybe, RequireAtLeastOne } from "./misc";
export interface LoadMore<TData, TVariables> {
loadMore: (
mergeFunc: (prev: TData, next: TData) => TData,
extraVariables: Partial<TVariables>
) => Promise<ApolloQueryResult<TData>>;
}
export type TypedQueryResult<TData, TVariables> = QueryResult<
TData,
TVariables
> &
LoadMore<TData, TVariables>;
export interface TypedQueryInnerProps<TData, TVariables> {
children: (result: TypedQueryResult<TData, TVariables>) => React.ReactNode;
displayLoader?: boolean;
skip?: boolean;
variables?: TVariables;
}
interface QueryProgressProps {
loading: boolean;
onLoading: () => void;
onCompleted: () => void;
}
class QueryProgress extends React.Component<QueryProgressProps, {}> {
componentDidMount() {
const { loading, onLoading } = this.props;
if (loading) {
onLoading();
}
}
componentDidUpdate(prevProps) {
const { loading, onLoading, onCompleted } = this.props;
if (prevProps.loading !== loading) {
if (loading) {
onLoading();
} else {
onCompleted();
}
}
}
render() {
return this.props.children;
}
}
// For some reason Query returns () => Element instead of () => ReactNode
export function TypedQuery<TData, TVariables>(
query: DocumentNode
): React.FC<TypedQueryInnerProps<TData, TVariables>> {
return ({ children, displayLoader, skip, variables }) => {
const pushMessage = useNotifier();
const [, dispatchAppState] = useAppState();
const intl = useIntl();
return (
<Query
fetchPolicy="cache-and-network"
query={query}
variables={variables}
skip={skip}
context={{ useBatching: true }}
errorPolicy="all"
>
{(queryData: QueryResult<TData, TVariables>) => {
if (queryData.error) {
if (
!queryData.error.graphQLErrors.every(
err =>
maybe(() => err.extensions.exception.code) ===
"PermissionDenied"
)
) {
pushMessage({
text: intl.formatMessage(commonMessages.somethingWentWrong)
});
}
}
const loadMore = (
mergeFunc: (
previousResults: TData,
fetchMoreResult: TData
) => TData,
extraVariables: RequireAtLeastOne<TVariables>
) =>
queryData.fetchMore({
query,
updateQuery: (previousResults, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return previousResults;
}
return mergeFunc(previousResults, fetchMoreResult);
},
variables: { ...variables, ...extraVariables }
});
if (displayLoader) {
return (
<QueryProgress
loading={queryData.loading}
onCompleted={() =>
dispatchAppState({
payload: {
value: false
},
type: "displayLoader"
})
}
onLoading={() =>
dispatchAppState({
payload: {
value: true
},
type: "displayLoader"
})
}
>
{children({
...queryData,
loadMore
})}
</QueryProgress>
);
}
return (
<>
{children({
...queryData,
loadMore
})}
</>
);
}}
</Query>
);
};
}
export const pageInfoFragment = gql`
fragment PageInfoFragment on PageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
`;