mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-22 20:25:38 +00:00
feat: enhance WebSocket message handling and styling
This commit is contained in:
@@ -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};
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user