import type { HttpRequest } from "@yaakapp-internal/models"; import { useAtom } from "jotai"; import { useCallback, useMemo } from "react"; import { useLocalStorage } from "react-use"; import { useIntrospectGraphQL } from "../../hooks/useIntrospectGraphQL"; import { useStateWithDeps } from "../../hooks/useStateWithDeps"; import { showDialog } from "../../lib/dialog"; import { Banner } from "../core/Banner"; import { Button } from "../core/Button"; import type { DropdownItem } from "../core/Dropdown"; import { Dropdown } from "../core/Dropdown"; import type { EditorProps } from "../core/Editor/Editor"; import { Editor } from "../core/Editor/LazyEditor"; import { FormattedError } from "../core/FormattedError"; import { Icon } from "../core/Icon"; import { Separator } from "../core/Separator"; import { tryFormatGraphql } from "../../lib/formatters"; import { showGraphQLDocExplorerAtom } from "./graphqlAtoms"; type Props = Pick & { baseRequest: HttpRequest; onChange: (body: HttpRequest["body"]) => void; request: HttpRequest; }; export function GraphQLEditor(props: Props) { // There's some weirdness with stale onChange being called when switching requests, so we'll // key on the request ID as a workaround for now. return ; } function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProps }: Props) { const [autoIntrospectDisabled, setAutoIntrospectDisabled] = useLocalStorage< Record >("graphQLAutoIntrospectDisabled", {}); const { schema, isLoading, error, refetch, clear } = useIntrospectGraphQL(baseRequest, { disabled: autoIntrospectDisabled?.[baseRequest.id], }); const [currentBody, setCurrentBody] = useStateWithDeps<{ query: string; variables: string | undefined; }>(() => { // Migrate text bodies to GraphQL format // NOTE: This is how GraphQL used to be stored if ("text" in request.body) { const b = tryParseJson(request.body.text, {}); const variables = JSON.stringify(b.variables || undefined, null, 2); return { query: b.query ?? "", variables }; } return { query: request.body.query ?? "", variables: request.body.variables ?? "" }; }, [extraEditorProps.forceUpdateKey]); const [isDocOpenRecord, setGraphqlDocStateAtomValue] = useAtom(showGraphQLDocExplorerAtom); const isDocOpen = isDocOpenRecord[request.id] !== undefined; const handleChangeQuery = useCallback( (query: string) => { setCurrentBody(({ variables }) => { const newBody = { query, variables }; onChange(newBody); return newBody; }); }, [onChange, setCurrentBody], ); const handleChangeVariables = useCallback( (variables: string) => { setCurrentBody(({ query }) => { const newBody = { query, variables: variables || undefined }; onChange(newBody); return newBody; }); }, [onChange, setCurrentBody], ); const actions = useMemo( () => [
{schema === undefined ? null /* Initializing */ : ( , }, { type: "separator" }, ] : []) satisfies DropdownItem[]), { hidden: !error, label: (

Schema introspection failed

), }); }} > View Error ), type: "content", }, { hidden: schema == null, label: `${isDocOpen ? "Hide" : "Show"} Documentation`, leftSlot: , onSelect: () => { setGraphqlDocStateAtomValue((v) => ({ ...v, [request.id]: isDocOpen ? undefined : null, })); }, }, { label: "Introspect Schema", leftSlot: , keepOpenOnSelect: true, onSelect: refetch, }, { type: "separator", label: "Setting" }, { label: "Automatic Introspection", keepOpenOnSelect: true, onSelect: () => { setAutoIntrospectDisabled({ ...autoIntrospectDisabled, [baseRequest.id]: !autoIntrospectDisabled?.[baseRequest.id], }); }, leftSlot: ( ), }, ]} > )}
, ], [ schema, clear, error, isDocOpen, isLoading, refetch, autoIntrospectDisabled, baseRequest.id, setGraphqlDocStateAtomValue, request.id, setAutoIntrospectDisabled, ], ); return (
Variables
); } function tryParseJson(text: string, fallback: unknown) { try { return JSON.parse(text); } catch { return fallback; } }