From 9e4e6435abdd11ee239ea3f37c55a7233a55981b Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Wed, 31 May 2023 21:29:41 -0700 Subject: [PATCH] Persist introspection queries and also improve --- src-web/components/GraphQLEditor.tsx | 19 ++++----- src-web/hooks/useIntrospectGraphQL.ts | 56 ++++++++++++++++++++------- src-web/lib/theme/window.ts | 4 +- 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src-web/components/GraphQLEditor.tsx b/src-web/components/GraphQLEditor.tsx index 2027e021..b0987c02 100644 --- a/src-web/components/GraphQLEditor.tsx +++ b/src-web/components/GraphQLEditor.tsx @@ -24,8 +24,7 @@ interface GraphQLBody { export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEditorProps }: Props) { const editorViewRef = useRef(null); - const introspection = useIntrospectGraphQL(baseRequest); - + const { schema, isLoading, error } = useIntrospectGraphQL(baseRequest); const { query, variables } = useMemo(() => { if (defaultValue === undefined) { return { query: '', variables: {} }; @@ -65,8 +64,8 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi // Refetch the schema when the URL changes useEffect(() => { if (editorViewRef.current === null) return; - updateSchema(editorViewRef.current, introspection.data); - }, [introspection.data]); + updateSchema(editorViewRef.current, schema); + }, [schema]); const dialog = useDialog(); @@ -81,22 +80,20 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi placeholder="..." ref={editorViewRef} actions={ - (introspection.error || introspection.isLoading) && ( + (error || isLoading) && ( ) } diff --git a/src-web/hooks/useIntrospectGraphQL.ts b/src-web/hooks/useIntrospectGraphQL.ts index e6674dab..317970b9 100644 --- a/src-web/hooks/useIntrospectGraphQL.ts +++ b/src-web/hooks/useIntrospectGraphQL.ts @@ -1,5 +1,6 @@ -import { useQuery } from '@tanstack/react-query'; -import type { GraphQLSchema } from 'graphql'; +import type { IntrospectionQuery } from 'graphql'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useLocalStorage } from 'react-use'; import { buildClientSchema, getIntrospectionQuery } from '../components/core/Editor'; import { minPromiseMillis } from '../lib/minPromiseMillis'; import type { HttpRequest } from '../lib/models'; @@ -13,18 +14,23 @@ const introspectionRequestBody = JSON.stringify({ }); export function useIntrospectGraphQL(baseRequest: HttpRequest) { - // Debounce the URL because it can change rapidly, and we don't - // want to send so many requests. + // Debounce the request because it can change rapidly and we don't + // want to send so too many requests. const request = useDebouncedValue(baseRequest); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(); + const [introspection, setIntrospection] = useLocalStorage( + `introspection:${baseRequest.id}`, + ); - return useQuery({ - queryKey: ['introspectGraphQL', { url: request.url, method: request.method }], - refetchInterval: 1000 * 60, // Refetch every minute - queryFn: async () => { - const response = await minPromiseMillis( - sendEphemeralRequest({ ...baseRequest, body: introspectionRequestBody }), - 700, - ); + const introspectionInterval = useRef(); + + useEffect(() => { + const fetchIntrospection = async () => { + setIsLoading(true); + setError(undefined); + const args = { ...baseRequest, body: introspectionRequestBody }; + const response = await minPromiseMillis(sendEphemeralRequest(args), 700); if (response.error) { return Promise.reject(new Error(response.error)); @@ -42,7 +48,27 @@ export function useIntrospectGraphQL(baseRequest: HttpRequest) { } const { data } = JSON.parse(body); - return buildClientSchema(data); - }, - }); + setIntrospection(data); + }; + + const runIntrospection = () => { + fetchIntrospection() + .catch((e) => setError(e.message)) + .finally(() => setIsLoading(false)); + }; + + // Do it again on an interval + clearInterval(introspectionInterval.current); + introspectionInterval.current = setInterval(runIntrospection, 1000 * 60); + runIntrospection(); // Run immediately + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [request.id, request.method]); + + const schema = useMemo( + () => (introspection ? buildClientSchema(introspection) : undefined), + [introspection], + ); + + return { schema, isLoading, error }; } diff --git a/src-web/lib/theme/window.ts b/src-web/lib/theme/window.ts index 0d7ebdee..9f665ef6 100644 --- a/src-web/lib/theme/window.ts +++ b/src-web/lib/theme/window.ts @@ -8,9 +8,9 @@ const darkTheme: AppTheme = { appearance: 'dark', layers: { root: { - blackPoint: 0.2, + blackPoint: 0.09, colors: { - gray: '#6b5b98', + gray: '#9b8ebe', red: '#ff417b', orange: '#fd9014', yellow: '#e8d13f',