import { type GrpcRequest, type HttpRequestHeader, patchModel } from "@yaakapp-internal/models"; import classNames from "classnames"; import type { CSSProperties } from "react"; import { useCallback, useMemo, useRef } from "react"; import { useAuthTab } from "../hooks/useAuthTab"; import { useContainerSize } from "../hooks/useContainerQuery"; import type { ReflectResponseService } from "../hooks/useGrpc"; import { useHeadersTab } from "../hooks/useHeadersTab"; import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey"; import { resolvedModelName } from "../lib/resolvedModelName"; import { Button } from "./core/Button"; import { CountBadge } from "./core/CountBadge"; import { Icon } from "./core/Icon"; import { IconButton } from "./core/IconButton"; import { PlainInput } from "./core/PlainInput"; import { RadioDropdown } from "./core/RadioDropdown"; import { HStack, VStack } from "./core/Stacks"; import type { TabItem } from "./core/Tabs/Tabs"; import { TabContent, Tabs } from "./core/Tabs/Tabs"; import { GrpcEditor } from "./GrpcEditor"; import { HeadersEditor } from "./HeadersEditor"; import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; import { MarkdownEditor } from "./MarkdownEditor"; import { UrlBar } from "./UrlBar"; interface Props { style?: CSSProperties; className?: string; activeRequest: GrpcRequest; protoFiles: string[]; reflectionError?: string; reflectionLoading?: boolean; methodType: | "unary" | "client_streaming" | "server_streaming" | "streaming" | "no-schema" | "no-method"; isStreaming: boolean; onCommit: () => void; onCancel: () => void; onSend: (v: { message: string }) => void; onGo: () => void; services: ReflectResponseService[] | null; } const TAB_MESSAGE = "message"; const TAB_METADATA = "metadata"; const TAB_AUTH = "auth"; const TAB_DESCRIPTION = "description"; export function GrpcRequestPane({ style, services, methodType, activeRequest, protoFiles, reflectionError, reflectionLoading, isStreaming, onGo, onCommit, onCancel, onSend, }: Props) { const authTab = useAuthTab(TAB_AUTH, activeRequest); const metadataTab = useHeadersTab(TAB_METADATA, activeRequest, "Metadata"); const inheritedHeaders = useInheritedHeaders(activeRequest); const forceUpdateKey = useRequestUpdateKey(activeRequest.id ?? null); const urlContainerEl = useRef(null); const { width: paneWidth } = useContainerSize(urlContainerEl); const handleChangeUrl = useCallback( (url: string) => patchModel(activeRequest, { url }), [activeRequest], ); const handleChangeMessage = useCallback( (message: string) => patchModel(activeRequest, { message }), [activeRequest], ); const select = useMemo(() => { const options = services?.flatMap((s) => s.methods.map((m) => ({ label: `${s.name.split(".").pop() ?? s.name}/${m.name}`, value: `${s.name}/${m.name}`, })), ) ?? []; const value = `${activeRequest?.service ?? ""}/${activeRequest?.method ?? ""}`; return { value, options }; }, [activeRequest?.method, activeRequest?.service, services]); const handleChangeService = useCallback( async (v: string) => { const [serviceName, methodName] = v.split("/", 2); if (serviceName == null || methodName == null) throw new Error("Should never happen"); await patchModel(activeRequest, { service: serviceName, method: methodName, }); }, [activeRequest], ); const handleConnect = useCallback(async () => { if (activeRequest == null) return; if (activeRequest.service == null || activeRequest.method == null) { alert({ id: "grpc-invalid-service-method", title: "Error", body: "Service or method not selected", }); } onGo(); }, [activeRequest, onGo]); const handleSend = useCallback(async () => { if (activeRequest == null) return; onSend({ message: activeRequest.message }); }, [activeRequest, onSend]); const tabs: TabItem[] = useMemo( () => [ { value: TAB_MESSAGE, label: "Message" }, ...metadataTab, ...authTab, { value: TAB_DESCRIPTION, label: "Info", rightSlot: activeRequest.description && , }, ], [activeRequest.description, authTab, metadataTab], ); const handleMetadataChange = useCallback( (metadata: HttpRequestHeader[]) => patchModel(activeRequest, { metadata }), [activeRequest], ); const handleDescriptionChange = useCallback( (description: string) => patchModel(activeRequest, { description }), [activeRequest], ); return (
0 && paneWidth < 400 && "!grid-cols-1", )} > ({ label: o.label, value: o.value, type: "default", shortLabel: o.label, }))} itemsAfter={[ { label: "Refresh", type: "default", leftSlot: , }, ]} > {methodType === "client_streaming" || methodType === "streaming" ? ( <> {isStreaming && ( <> )} ) : ( )}
patchModel(activeRequest, { name })} />
); }