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 AppProgress from "./components/AppProgress"; import NotFoundPage from "./components/NotFoundPage"; import useNavigator from "./hooks/useNavigator"; import useNotifier from "./hooks/useNotifier"; import { commonMessages } from "./intl"; import { maybe, RequireAtLeastOne } from "./misc"; export interface LoadMore { loadMore: ( mergeFunc: (prev: TData, next: TData) => TData, extraVariables: Partial ) => Promise>; } export type TypedQueryResult = QueryResult< TData, TVariables > & LoadMore; export interface TypedQueryInnerProps { children: (result: TypedQueryResult) => React.ReactNode; displayLoader?: boolean; skip?: boolean; variables?: TVariables; require?: Array; } interface QueryProgressProps { loading: boolean; onLoading: () => void; onCompleted: () => void; } class QueryProgress extends React.Component { 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( query: DocumentNode ): React.FC> { return ({ children, displayLoader, skip, variables, require }) => { const navigate = useNavigator(); const pushMessage = useNotifier(); const intl = useIntl(); return ( {({ setProgressState }) => ( {(queryData: QueryResult) => { 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 ) => queryData.fetchMore({ query, updateQuery: (previousResults, { fetchMoreResult }) => { if (!fetchMoreResult) { return previousResults; } return mergeFunc(previousResults, fetchMoreResult); }, variables: { ...variables, ...extraVariables } }); let childrenOrNotFound = children({ ...queryData, loadMore }); if ( !queryData.loading && require && queryData.data && !require.reduce( (acc, key) => acc && queryData.data[key] !== null, true ) ) { childrenOrNotFound = ( navigate("/")} /> ); } if (displayLoader) { return ( setProgressState(false)} onLoading={() => setProgressState(true)} > {childrenOrNotFound} ); } return <>{childrenOrNotFound}; }} )} ); }; } export const pageInfoFragment = gql` fragment PageInfoFragment on PageInfo { endCursor hasNextPage hasPreviousPage startCursor } `;