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) ? ( - + ) : (