diff --git a/desktop/flipper-plugin/src/ui/Panel.tsx b/desktop/flipper-plugin/src/ui/Panel.tsx index 8e94885d4..7588a423f 100644 --- a/desktop/flipper-plugin/src/ui/Panel.tsx +++ b/desktop/flipper-plugin/src/ui/Panel.tsx @@ -28,6 +28,7 @@ export const Panel: React.FC<{ collapsed?: boolean; pad?: Spacing; gap?: Spacing; + extraActions?: React.ReactElement | null; }> = (props) => { const [collapsed, setCollapsed] = useLocalStorageState( `panel:${props.title}:collapsed`, @@ -49,7 +50,16 @@ export const Panel: React.FC<{ onChange={toggle}> + {props.title} + {props.extraActions} + + ) : ( + props.title + ) + } showArrow={props.collapsible !== false}> {props.children} diff --git a/desktop/plugins/public/network/RequestDetails.tsx b/desktop/plugins/public/network/RequestDetails.tsx index a68114e8e..65521400d 100644 --- a/desktop/plugins/public/network/RequestDetails.tsx +++ b/desktop/plugins/public/network/RequestDetails.tsx @@ -23,11 +23,18 @@ import { } from 'flipper-plugin'; import {Select, Typography} from 'antd'; -import {bodyAsBinary, bodyAsString, formatBytes, getHeaderValue} from './utils'; +import { + bodyAsBinary, + bodyAsString, + formatBytes, + getHeaderValue, + isTextual, +} from './utils'; import {Request, Header, Insights, RetryInsights} from './types'; import {BodyOptions} from './index'; import {ProtobufDefinitionsRepository} from './ProtobufDefinitionsRepository'; import {KeyValueItem, KeyValueTable} from './KeyValueTable'; +import {CopyOutlined} from '@ant-design/icons'; const {Text} = Typography; @@ -35,6 +42,7 @@ type RequestDetailsProps = { request: Request; bodyFormat: string; onSelectFormat: (bodyFormat: string) => void; + onCopyText(test: string): void; }; export default class RequestDetails extends Component { urlColumns = (url: URL) => { @@ -59,7 +67,7 @@ export default class RequestDetails extends Component { }; render() { - const {request, bodyFormat, onSelectFormat} = this.props; + const {request, bodyFormat, onSelectFormat, onCopyText} = this.props; const url = new URL(request.url); const formattedText = bodyFormat == 'formatted'; @@ -83,7 +91,21 @@ export default class RequestDetails extends Component { ) : null} {request.requestData != null ? ( - + { + e.stopPropagation(); + onCopyText(request.requestData as string); + }} + /> + ) : null + } + pad> { title={`Response Body${ request.responseIsMock ? ' (Mocked)' : '' }`} + extraActions={ + isTextual(request.responseHeaders) && request.responseData ? ( + { + e.stopPropagation(); + onCopyText(request.responseData as string); + }} + /> + ) : null + } pad> ) { ); }, + onCopyText(text: string) { + client.writeTextToClipboard(text); + message.success('Text copied to clipboard'); + }, }; } @@ -418,6 +422,7 @@ function Sidebar() { request={request} bodyFormat={detailBodyFormat} onSelectFormat={instance.onSelectFormat} + onCopyText={instance.onCopyText} /> ); }