import useResizeObserver from '@react-hook/resize-observer'; import classNames from 'classnames'; import type { CSSProperties, FormEvent } from 'react'; import React, { useCallback, useMemo, useRef, useState } from 'react'; import { createGlobalState } from 'react-use'; import type { ReflectResponseService } from '../hooks/useGrpc'; import { useGrpcConnections } from '../hooks/useGrpcConnections'; import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest'; import type { GrpcRequest } from '../lib/models'; import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/models'; import { Button } from './core/Button'; import { Icon } from './core/Icon'; import { IconButton } from './core/IconButton'; 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 { UrlBar } from './UrlBar'; interface Props { style?: CSSProperties; className?: string; activeRequest: GrpcRequest; reflectionError?: string; reflectionLoading?: boolean; methodType: | 'unary' | 'client_streaming' | 'server_streaming' | 'streaming' | 'no-schema' | 'no-method'; onUnary: () => void; onCommit: () => void; onCancel: () => void; onSend: (v: { message: string }) => void; onClientStreaming: () => void; onServerStreaming: () => void; onStreaming: () => void; services: ReflectResponseService[] | null; } const useActiveTab = createGlobalState('message'); export function GrpcConnectionSetupPane({ style, services, methodType, activeRequest, reflectionError, reflectionLoading, onStreaming, onClientStreaming, onServerStreaming, onCommit, onCancel, onSend, onUnary, }: Props) { const connections = useGrpcConnections(activeRequest.id ?? null); const updateRequest = useUpdateGrpcRequest(activeRequest?.id ?? null); const activeConnection = connections[0] ?? null; const isStreaming = activeConnection?.elapsed === 0; const [activeTab, setActiveTab] = useActiveTab(); const [paneSize, setPaneSize] = useState(99999); const urlContainerEl = useRef(null); useResizeObserver(urlContainerEl.current, (entry) => { setPaneSize(entry.contentRect.width); }); const handleChangeUrl = useCallback( (url: string) => updateRequest.mutateAsync({ url }), [updateRequest], ); const handleChangeMessage = useCallback( (message: string) => updateRequest.mutateAsync({ message }), [updateRequest], ); const select = useMemo(() => { const options = services?.flatMap((s) => s.methods.map((m) => ({ label: `${s.name.split('.', 2).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 updateRequest.mutateAsync({ service: serviceName, method: methodName, }); }, [updateRequest], ); const handleConnect = useCallback( async (e: FormEvent) => { e.preventDefault(); 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', }); } if (methodType === 'streaming') { onStreaming(); } else if (methodType === 'server_streaming') { onServerStreaming(); } else if (methodType === 'client_streaming') { onClientStreaming(); } else { onUnary(); } }, [activeRequest, methodType, onStreaming, onServerStreaming, onClientStreaming, onUnary], ); const tabs: TabItem[] = useMemo( () => [ { value: 'message', label: 'Message' }, { value: 'auth', label: 'Auth', options: { value: AUTH_TYPE_NONE, // TODO items: [ { label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC }, { label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER }, { type: 'separator' }, { label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE }, ], onChange: async (authenticationType) => { // TODO }, }, }, { value: 'metadata', label: 'Metadata' }, ], [], ); return (
({ label: o.label, value: o.value, type: 'default', shortLabel: o.label, }))} extraItems={[ { type: 'separator' }, { label: 'Refresh', type: 'default', key: 'custom', leftSlot: , }, ]} > {!isStreaming && ( )} {isStreaming && ( )} {methodType === 'client_streaming' && isStreaming && ( )} {(methodType === 'client_streaming' || methodType === 'streaming') && isStreaming && ( onSend({ message: activeRequest.message ?? '' })} icon="sendHorizontal" /> )}
); }