diff --git a/packages/insomnia/src/ui/components/websocket-request-pane.tsx b/packages/insomnia/src/ui/components/websocket-request-pane.tsx
index 076463d1708..0bbc9bdd5d9 100644
--- a/packages/insomnia/src/ui/components/websocket-request-pane.tsx
+++ b/packages/insomnia/src/ui/components/websocket-request-pane.tsx
@@ -1,5 +1,20 @@
-import React, { FC } from 'react';
+import React, { FunctionComponent } from 'react';
+import styled from 'styled-components';
-export const WebSocketRequestPane: FC = () => {
- return
WebSocket Request
;
+import { Pane, PaneHeader } from './panes/pane';
+import { WebsocketActionBar } from './websockets/action-bar';
+
+interface Props {
+ requestId: string;
+}
+
+const StretchedPaneHeader = styled(PaneHeader)({ '&&': { alignItems: 'stretch' } });
+export const WebSocketRequestPane: FunctionComponent = ({ requestId }) => {
+ return (
+
+
+
+
+
+ );
};
diff --git a/packages/insomnia/src/ui/components/websockets/action-bar.tsx b/packages/insomnia/src/ui/components/websockets/action-bar.tsx
new file mode 100644
index 00000000000..7365ea3d5dd
--- /dev/null
+++ b/packages/insomnia/src/ui/components/websockets/action-bar.tsx
@@ -0,0 +1,102 @@
+import React, { FunctionComponent, useEffect } from 'react';
+import styled from 'styled-components';
+
+import { ReadyState } from './types';
+import { useWSControl } from './use-ws-control';
+import { useWSReadyState } from './use-ws-ready-state';
+
+const Button = styled.button({
+ paddingRight: 'var(--padding-md)',
+ paddingLeft: 'var(--padding-md)',
+ textAlign: 'center',
+ background: 'var(--color-surprise)',
+ color: 'var(--color-font-surprise)',
+ flex: '0 0 100px',
+});
+
+interface ActionButtonProps {
+ requestId: string;
+ readyState: ReadyState;
+}
+const ActionButton: FunctionComponent = ({ requestId, readyState }) => {
+ const { close } = useWSControl(requestId);
+
+ if (readyState === ReadyState.CONNECTING || readyState === ReadyState.CLOSED) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+interface ActionBarProps {
+ requestId: string;
+}
+
+const Form = styled.form({
+ flex: 1,
+ display: 'flex',
+});
+
+const Input = styled.input({
+ boxSizing: 'border-box',
+ width: '100%',
+ height: '100%',
+ paddingRight: 'var(--padding-md)',
+ paddingLeft: 'var(--padding-md)',
+});
+
+const WebSocketIcon = styled.span({
+ color: 'var(--color-notice)',
+ display: 'flex',
+ alignItems: 'center',
+ paddingRight: 'var(--padding-md)',
+ paddingLeft: 'var(--padding-md)',
+});
+
+export const WebsocketActionBar: FunctionComponent = ({ requestId }) => {
+ const { connect, close } = useWSControl(requestId);
+ const readyState = useWSReadyState(requestId);
+
+ const handleSubmit = (e: any) => {
+ e.preventDefault();
+ const formData = new FormData(e.currentTarget);
+ const url = (formData.get('websocketUrlInput') as string) || '';
+ connect(url);
+ };
+
+ useEffect(() => {
+ close();
+ }, [close]);
+
+ return (
+ <>
+ WS
+
+
+ >
+ );
+};
diff --git a/packages/insomnia/src/ui/components/websockets/types.ts b/packages/insomnia/src/ui/components/websockets/types.ts
new file mode 100644
index 00000000000..98980e62b70
--- /dev/null
+++ b/packages/insomnia/src/ui/components/websockets/types.ts
@@ -0,0 +1,6 @@
+export enum ReadyState {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSING = 2,
+ CLOSED = 3,
+}
diff --git a/packages/insomnia/src/ui/components/websockets/use-ws-control.ts b/packages/insomnia/src/ui/components/websockets/use-ws-control.ts
new file mode 100644
index 00000000000..0620d533eb1
--- /dev/null
+++ b/packages/insomnia/src/ui/components/websockets/use-ws-control.ts
@@ -0,0 +1,38 @@
+import { useCallback } from 'react';
+
+import * as models from '../../../models';
+
+interface UseWSControl {
+ send: (message: string | Blob | ArrayBufferLike | ArrayBufferView) => void;
+ connect: (url: string) => void;
+ close: (code?: number, reason?: string) => void;
+}
+
+// TODO: replace the window.main.webSocketConnection.methods with client class or object
+export function useWSControl(requestId: string): UseWSControl {
+ const send = useCallback((message: string | Blob | ArrayBufferLike | ArrayBufferView) => {
+ window.main.webSocketConnection.event.send({
+ requestId,
+ // TODO: handle types later
+ message: JSON.stringify(message),
+ });
+ }, [requestId]);
+
+ const connect = useCallback(async (url: string) => {
+ const wsr = await models.websocketRequest.getById(requestId);
+ if (wsr) {
+ await models.websocketRequest.update(wsr, { url });
+ await window.main.webSocketConnection.create({ requestId });
+ }
+ }, [requestId]);
+
+ const close = useCallback(() => {
+ window.main.webSocketConnection.close({ requestId });
+ }, [requestId]);
+
+ return {
+ send,
+ connect,
+ close,
+ };
+}
diff --git a/packages/insomnia/src/ui/components/websockets/use-ws-ready-state.ts b/packages/insomnia/src/ui/components/websockets/use-ws-ready-state.ts
new file mode 100644
index 00000000000..2061aa47068
--- /dev/null
+++ b/packages/insomnia/src/ui/components/websockets/use-ws-ready-state.ts
@@ -0,0 +1,20 @@
+import { useEffect, useState } from 'react';
+
+import { ReadyState } from './types';
+
+export function useWSReadyState(requestId: string): ReadyState {
+ const [readyState, setReadyState] = useState(ReadyState.CONNECTING);
+
+ useEffect(() => {
+ const unsubscribe = window.main.webSocketConnection.readyState.subscribe(
+ { requestId },
+ (incomingReadyState: ReadyState) => {
+ setReadyState(incomingReadyState);
+ }
+ );
+
+ return unsubscribe;
+ }, [requestId]);
+
+ return readyState;
+}
diff --git a/packages/insomnia/src/ui/components/wrapper-debug.tsx b/packages/insomnia/src/ui/components/wrapper-debug.tsx
index aaf44f12d21..9dbe6f3292d 100644
--- a/packages/insomnia/src/ui/components/wrapper-debug.tsx
+++ b/packages/insomnia/src/ui/components/wrapper-debug.tsx
@@ -128,7 +128,7 @@ export const WrapperDebug: FC = ({
/>
) : (
isWebSocketRequest(activeRequest) ? (
-
+
) : (