From 1a33f2e83fc4e00220de86bc882e4098e1b9fc6a Mon Sep 17 00:00:00 2001 From: Siddharth Gelera Date: Thu, 18 Sep 2025 15:14:08 +0530 Subject: [PATCH] feat: enhance WebSocket message handling and styling --- .../WSMessagesList/StyledWrapper.js | 8 ++++ .../WsResponsePane/WSMessagesList/index.js | 39 ++++++++++++------- .../ReduxStore/slices/collections/index.js | 25 ++++++------ packages/bruno-requests/src/ws/ws-client.js | 2 +- 4 files changed, 48 insertions(+), 26 deletions(-) diff --git a/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/StyledWrapper.js index 27e4b6e98..733c644d4 100644 --- a/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/StyledWrapper.js +++ b/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/StyledWrapper.js @@ -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}; diff --git a/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/index.js b/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/index.js index 1d0ecf419..bea82b4f8 100644 --- a/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/index.js +++ b/packages/bruno-app/src/components/ResponsePane/WsResponsePane/WSMessagesList/index.js @@ -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: , outgoing: , - info: + info: , + error: }[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 (
{ @@ -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 }) => { @@ -125,15 +136,17 @@ const WSMessageItem = ({ message, inFocus }) => { {message.timestamp && ( {new Date(message.timestamp).toISOString()} )} - {!isInfo && ( - - {isOpen ? ( - - ) : ( - - )} - - )} + {canOpenMessage + ? ( + + {isOpen ? ( + + ) : ( + + )} + + ) + : }
{isOpen && ( diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index fd27fdadd..2bb3c3da7 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -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': diff --git a/packages/bruno-requests/src/ws/ws-client.js b/packages/bruno-requests/src/ws/ws-client.js index ca5106d0a..f8e375978 100644 --- a/packages/bruno-requests/src/ws/ws-client.js +++ b/packages/bruno-requests/src/ws/ws-client.js @@ -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);