feat: enhance WebSocket message handling and styling

This commit is contained in:
Siddharth Gelera
2025-09-18 15:14:08 +05:30
parent 875060155a
commit 1a33f2e83f
4 changed files with 48 additions and 26 deletions

View File

@@ -7,6 +7,14 @@ const StyledWrapper = styled.div`
background-color: ${({theme}) => theme.table.striped};
}
.ws-message:not(:last-child) {
border-bottom: 1px solid ${({theme}) => theme.table.border};
}
.ws-message:not(:last-child).open {
border-bottom-width: 0px;
}
.ws-incoming {
background: ${(props) => props.theme.bg};
border-color: ${(props) => props.theme.table.border};

View File

@@ -1,7 +1,7 @@
import React from 'react';
import classnames from 'classnames';
import StyledWrapper from './StyledWrapper';
import { IconChevronUp, IconInfoCircle, IconChevronDown, IconArrowUpRight, IconArrowDownLeft } from '@tabler/icons';
import { IconExclamationCircle, IconChevronRight, IconInfoCircle, IconChevronDown, IconArrowUpRight, IconArrowDownLeft } from '@tabler/icons';
import CodeEditor from 'components/CodeEditor/index';
import { useTheme } from 'providers/Theme';
import { useState } from 'react';
@@ -57,7 +57,8 @@ const TypeIcon = ({ type }) => {
return {
incoming: <IconArrowDownLeft {...commonProps} />,
outgoing: <IconArrowUpRight {...commonProps} />,
info: <IconInfoCircle {...commonProps} />
info: <IconInfoCircle {...commonProps} />,
error: <IconExclamationCircle {...commonProps} />
}[type];
};
@@ -71,6 +72,8 @@ const WSMessageItem = ({ message, inFocus }) => {
const isIncoming = message.type === 'incoming';
const isInfo = message.type === 'info';
const isError = message.type === 'error';
const isOutgoing = message.type === 'outgoing';
let contentHexdump = message.messageHexdump;
let parsedContent = parseContent(message.message);
const dataType = getDataTypeText(parsedContent.type);
@@ -87,6 +90,8 @@ const WSMessageItem = ({ message, inFocus }) => {
}
}, [message]);
const canOpenMessage = !isInfo && !isError
return (
<div
ref={(node) => {
@@ -106,7 +111,7 @@ const WSMessageItem = ({ message, inFocus }) => {
'cursor-not-allowed': isInfo
})}
onClick={(e) => {
if (isInfo) return;
if (!canOpenMessage) return;
setIsOpen(!isOpen);
}}
>
@@ -114,7 +119,13 @@ const WSMessageItem = ({ message, inFocus }) => {
<span
className={classnames(
'font-semibold flex items-center gap-1',
isIncoming ? 'text-blue-700' : 'text-green-700'
{
'text-green-700': isIncoming,
'text-yellow-700': isOutgoing,
'text-blue-700': isInfo,
'text-red-700': isError,
'text-red-700': isError,
}
)}
>
<TypeIcon type={message.type} />
@@ -125,15 +136,17 @@ const WSMessageItem = ({ message, inFocus }) => {
{message.timestamp && (
<span className="text-xs text-gray-400">{new Date(message.timestamp).toISOString()}</span>
)}
{!isInfo && (
<span className="text-gray-600">
{isOpen ? (
<IconChevronDown size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
) : (
<IconChevronUp size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
)}
</span>
)}
{canOpenMessage
? (
<span className="text-gray-600">
{isOpen ? (
<IconChevronDown size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
) : (
<IconChevronRight size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
)}
</span>
)
: <span class="w-4"></span>}
</div>
</div>
{isOpen && (

View File

@@ -2856,18 +2856,12 @@ export const collectionsSlice = createSlice({
updatedResponse.statusCode = code;
updatedResponse.statusText = wsStatusCodes[code] || 'CLOSED';
updatedResponse.statusDescription = reason;
// Handle error status (non-normal closure)
if (code !== 1000) {
updatedResponse.isError = true;
updatedResponse.error = reason || `WebSocket closed with code ${code}`;
}else{
updatedResponse.responses.push({
type: "info",
message: "Closed",
timestamp: Date.now()
})
}
updatedResponse.responses.push({
type: code !== 1000 ? 'info' : 'error',
message: reason.trim().length ? ['Closed:',reason.trim()].join(' ') : 'Closed',
timestamp,
})
break;
case 'error':
@@ -2876,6 +2870,13 @@ export const collectionsSlice = createSlice({
updatedResponse.error = errorDetails || 'WebSocket error occurred';
updatedResponse.status = 'ERROR';
updatedResponse.statusText = 'ERROR';
updatedResponse.responses.push({
type: 'error',
message: errorDetails || 'WebSocket error occurred',
timestamp,
})
break;
case 'connecting':

View File

@@ -311,7 +311,7 @@ class WsClient {
ws.on('close', (code, reason) => {
this.eventCallback('ws:close', requestId, collectionUid, {
code,
reason: reason.toString(),
reason: Buffer.from(reason).toString(),
timestamp: Date.now()
});
this.#removeConnection(requestId);