import classNames from 'classnames'; import { format } from 'date-fns'; import type { CSSProperties } from 'react'; import React, { useEffect, useMemo, useState } from 'react'; import { useGrpcConnections } from '../hooks/useGrpcConnections'; import { useGrpcEvents } from '../hooks/useGrpcEvents'; import type { GrpcEvent, GrpcRequest } from '../lib/models'; import { Icon } from './core/Icon'; import { JsonAttributeTree } from './core/JsonAttributeTree'; import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; import { Separator } from './core/Separator'; import { SplitLayout } from './core/SplitLayout'; import { HStack } from './core/Stacks'; import { EmptyStateText } from './EmptyStateText'; import { RecentConnectionsDropdown } from './RecentConnectionsDropdown'; interface Props { style?: CSSProperties; className?: string; activeRequest: GrpcRequest; methodType: | 'unary' | 'client_streaming' | 'server_streaming' | 'streaming' | 'no-schema' | 'no-method'; } export function GrpcConnectionMessagesPane({ style, methodType, activeRequest }: Props) { const [activeEventId, setActiveEventId] = useState(null); const connections = useGrpcConnections(activeRequest.id ?? null); const activeConnection = connections[0] ?? null; const events = useGrpcEvents(activeConnection?.id ?? null); const activeEvent = useMemo( () => events.find((m) => m.id === activeEventId) ?? null, [activeEventId, events], ); // Set active message to the first message received if unary useEffect(() => { if (events.length === 0 || activeEvent != null || methodType !== 'unary') { return; } setActiveEventId(events.find((m) => m.eventType === 'server_message')?.id ?? null); // eslint-disable-next-line react-hooks/exhaustive-deps }, [events.length]); return ( activeConnection && (
{events.length} messages {activeConnection.elapsed === 0 && ( )} { // todo }} />
{...events.map((e) => ( { if (e.id === activeEventId) setActiveEventId(null); else setActiveEventId(e.id); }} /> ))}
) } secondSlot={ activeEvent && (() => (
{activeEvent.eventType === 'client_message' || activeEvent.eventType === 'server_message' ? ( <>
Message {activeEvent.eventType === 'client_message' ? 'Sent' : 'Received'}
) : (
{activeEvent.error ?? activeEvent.content}
{Object.keys(activeEvent.metadata).length === 0 ? ( No {activeEvent.eventType === 'connection_end' ? 'trailers' : 'metadata'} ) : ( {Object.entries(activeEvent.metadata).map(([key, value]) => ( ))} )}
)}
)) } /> ); } function EventRow({ onClick, isActive, event, }: { onClick?: () => void; isActive?: boolean; event: GrpcEvent; }) { const { eventType, status, createdAt, content, error } = event; return ( ); } const GRPC_CODES: Record = { 0: 'Ok', 1: 'Cancelled', 2: 'Unknown', 3: 'Invalid argument', 4: 'Deadline exceeded', 5: 'Not found', 6: 'Already exists', 7: 'Permission denied', 8: 'Resource exhausted', 9: 'Failed precondition', 10: 'Aborted', 11: 'Out of range', 12: 'Unimplemented', 13: 'Internal', 14: 'Unavailable', 15: 'Data loss', 16: 'Unauthenticated', };