import { DocumentNode } from "graphql"; import gql from "graphql-tag"; import React from "react"; import { Query, QueryResult } from "react-apollo"; import { ApolloQueryResult } from "apollo-client"; import AppProgress from "./components/AppProgress"; import ErrorPage from "./components/ErrorPage/ErrorPage"; import useNavigator from "./hooks/useNavigator"; import useNotifier from "./hooks/useNotifier"; import i18n from "./i18n"; import { RequireAtLeastOne } from "./misc"; export interface LoadMore { loadMore: ( mergeFunc: (prev: TData, next: TData) => TData, extraVariables: RequireAtLeastOne ) => 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; } } export function TypedQuery( query: DocumentNode ): React.FC> { class StrictTypedQuery extends Query {} return props => { const navigate = useNavigator(); const pushMessage = useNotifier(); return ( {({ setProgressState }) => { // Obviously, this is workaround to the problem described here: // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/32588 const { children, displayLoader, skip, variables, require } = props as JSX.LibraryManagedAttributes< typeof StrictTypedQuery, typeof props >; return ( {queryData => { if (queryData.error) { const msg = i18n.t("Something went wrong: {{ message }}", { message: queryData.error.message }); pushMessage({ text: msg }); } 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 } `;