Convert Flipper plugin "Network" to TypeScript
Summary: _typescript_ Reviewed By: danielbuechele Differential Revision: D17155509 fbshipit-source-id: 45ae3e2de8cd7b3cdf7271905ef7c318d4289391
This commit is contained in:
committed by
Facebook Github Bot
parent
0a53cccb40
commit
705ba8eaa8
@@ -5,13 +5,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {
|
||||
Request,
|
||||
Response,
|
||||
Header,
|
||||
Insights,
|
||||
RetryInsights,
|
||||
} from './types.tsx';
|
||||
import {Request, Response, Header, Insights, RetryInsights} from './types';
|
||||
|
||||
import {
|
||||
Component,
|
||||
@@ -24,11 +18,11 @@ import {
|
||||
styled,
|
||||
colors,
|
||||
} from 'flipper';
|
||||
import {decodeBody, getHeaderValue} from './utils.tsx';
|
||||
import {formatBytes} from './index.js';
|
||||
import {decodeBody, getHeaderValue} from './utils';
|
||||
import {formatBytes} from './index';
|
||||
import React from 'react';
|
||||
|
||||
import querystring from 'querystring';
|
||||
// $FlowFixMe
|
||||
import xmlBeautifier from 'xml-beautifier';
|
||||
|
||||
const WrappingText = styled(Text)({
|
||||
@@ -55,17 +49,17 @@ const KeyValueColumns = {
|
||||
};
|
||||
|
||||
type RequestDetailsProps = {
|
||||
request: Request,
|
||||
response: ?Response,
|
||||
request: Request;
|
||||
response: Response | null | undefined;
|
||||
};
|
||||
|
||||
type RequestDetailsState = {
|
||||
bodyFormat: string,
|
||||
bodyFormat: string;
|
||||
};
|
||||
|
||||
export default class RequestDetails extends Component<
|
||||
RequestDetailsProps,
|
||||
RequestDetailsState,
|
||||
RequestDetailsState
|
||||
> {
|
||||
static Container = styled(FlexColumn)({
|
||||
height: '100%',
|
||||
@@ -219,21 +213,21 @@ class QueryInspector extends Component<{queryParams: URLSearchParams}> {
|
||||
render() {
|
||||
const {queryParams} = this.props;
|
||||
|
||||
const rows = [];
|
||||
for (const kv of queryParams.entries()) {
|
||||
const rows: any = [];
|
||||
queryParams.forEach((value: string, key: string) => {
|
||||
rows.push({
|
||||
columns: {
|
||||
key: {
|
||||
value: <WrappingText>{kv[0]}</WrappingText>,
|
||||
value: <WrappingText>{key}</WrappingText>,
|
||||
},
|
||||
value: {
|
||||
value: <WrappingText>{kv[1]}</WrappingText>,
|
||||
value: <WrappingText>{value}</WrappingText>,
|
||||
},
|
||||
},
|
||||
copyText: kv[1],
|
||||
key: kv[0],
|
||||
copyText: value,
|
||||
key: key,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return rows.length > 0 ? (
|
||||
<ManagedTable
|
||||
@@ -250,37 +244,40 @@ class QueryInspector extends Component<{queryParams: URLSearchParams}> {
|
||||
}
|
||||
|
||||
type HeaderInspectorProps = {
|
||||
headers: Array<Header>,
|
||||
headers: Array<Header>;
|
||||
};
|
||||
|
||||
type HeaderInspectorState = {
|
||||
computedHeaders: Object,
|
||||
computedHeaders: Object;
|
||||
};
|
||||
|
||||
class HeaderInspector extends Component<
|
||||
HeaderInspectorProps,
|
||||
HeaderInspectorState,
|
||||
HeaderInspectorState
|
||||
> {
|
||||
render() {
|
||||
const computedHeaders = this.props.headers.reduce((sum, header) => {
|
||||
return {...sum, [header.key]: header.value};
|
||||
}, {});
|
||||
const computedHeaders: Map<string, string> = this.props.headers.reduce(
|
||||
(sum, header) => {
|
||||
return sum.set(header.key, header.value);
|
||||
},
|
||||
new Map(),
|
||||
);
|
||||
|
||||
const rows = [];
|
||||
for (const key in computedHeaders) {
|
||||
const rows: any = [];
|
||||
computedHeaders.forEach((value: string, key: string) => {
|
||||
rows.push({
|
||||
columns: {
|
||||
key: {
|
||||
value: <WrappingText>{key}</WrappingText>,
|
||||
},
|
||||
value: {
|
||||
value: <WrappingText>{computedHeaders[key]}</WrappingText>,
|
||||
value: <WrappingText>{value}</WrappingText>,
|
||||
},
|
||||
},
|
||||
copyText: computedHeaders[key],
|
||||
copyText: value,
|
||||
key,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return rows.length > 0 ? (
|
||||
<ManagedTable
|
||||
@@ -302,13 +299,13 @@ const BodyContainer = styled('div')({
|
||||
});
|
||||
|
||||
type BodyFormatter = {
|
||||
formatRequest?: (request: Request) => any,
|
||||
formatResponse?: (request: Request, response: Response) => any,
|
||||
formatRequest?: (request: Request) => any;
|
||||
formatResponse?: (request: Request, response: Response) => any;
|
||||
};
|
||||
|
||||
class RequestBodyInspector extends Component<{
|
||||
request: Request,
|
||||
formattedText: boolean,
|
||||
request: Request;
|
||||
formattedText: boolean;
|
||||
}> {
|
||||
render() {
|
||||
const {request, formattedText} = this.props;
|
||||
@@ -337,9 +334,9 @@ class RequestBodyInspector extends Component<{
|
||||
}
|
||||
|
||||
class ResponseBodyInspector extends Component<{
|
||||
response: Response,
|
||||
request: Request,
|
||||
formattedText: boolean,
|
||||
response: Response;
|
||||
request: Request;
|
||||
formattedText: boolean;
|
||||
}> {
|
||||
render() {
|
||||
const {request, response, formattedText} = this.props;
|
||||
@@ -374,12 +371,12 @@ const MediaContainer = styled(FlexColumn)({
|
||||
});
|
||||
|
||||
type ImageWithSizeProps = {
|
||||
src: string,
|
||||
src: string;
|
||||
};
|
||||
|
||||
type ImageWithSizeState = {
|
||||
width: number,
|
||||
height: number,
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
||||
@@ -394,7 +391,7 @@ class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
||||
fontSize: 14,
|
||||
});
|
||||
|
||||
constructor(props, context) {
|
||||
constructor(props: ImageWithSizeProps, context: any) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
width: 0,
|
||||
@@ -595,7 +592,7 @@ class LogEventFormatter {
|
||||
formatRequest = (request: Request) => {
|
||||
if (request.url.indexOf('logging_client_event') > 0) {
|
||||
const data = querystring.parse(decodeBody(request));
|
||||
if (data.message) {
|
||||
if (typeof data.message === 'string') {
|
||||
data.message = JSON.parse(data.message);
|
||||
}
|
||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||
@@ -607,7 +604,7 @@ class GraphQLBatchFormatter {
|
||||
formatRequest = (request: Request) => {
|
||||
if (request.url.indexOf('graphqlbatch') > 0) {
|
||||
const data = querystring.parse(decodeBody(request));
|
||||
if (data.queries) {
|
||||
if (typeof data.queries === 'string') {
|
||||
data.queries = JSON.parse(data.queries);
|
||||
}
|
||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||
@@ -643,10 +640,10 @@ class GraphQLFormatter {
|
||||
formatRequest = (request: Request) => {
|
||||
if (request.url.indexOf('graphql') > 0) {
|
||||
const data = querystring.parse(decodeBody(request));
|
||||
if (data.variables) {
|
||||
if (typeof data.variables === 'string') {
|
||||
data.variables = JSON.parse(data.variables);
|
||||
}
|
||||
if (data.query_params) {
|
||||
if (typeof data.query_params === 'string') {
|
||||
data.query_params = JSON.parse(data.query_params);
|
||||
}
|
||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||
@@ -745,7 +742,11 @@ class InsightsInspector extends Component<{insights: Insights}> {
|
||||
} ${timesWord} out of ${retry.limit})`;
|
||||
}
|
||||
|
||||
buildRow<T>(name: string, value: ?T, formatter: T => string): any {
|
||||
buildRow<T>(
|
||||
name: string,
|
||||
value: T | null | undefined,
|
||||
formatter: (value: T) => string,
|
||||
): any {
|
||||
return value
|
||||
? {
|
||||
columns: {
|
||||
@@ -5,13 +5,10 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {
|
||||
TableHighlightedRows,
|
||||
TableRows,
|
||||
TableBodyRow,
|
||||
MetricType,
|
||||
} from 'flipper';
|
||||
import {TableHighlightedRows, TableRows, TableBodyRow} from 'flipper';
|
||||
import {padStart} from 'lodash';
|
||||
import React from 'react';
|
||||
import {MenuItemConstructorOptions} from 'electron';
|
||||
|
||||
import {
|
||||
ContextMenu,
|
||||
@@ -26,25 +23,21 @@ import {
|
||||
SearchableTable,
|
||||
FlipperPlugin,
|
||||
} from 'flipper';
|
||||
import type {Request, RequestId, Response} from './types.tsx';
|
||||
import {
|
||||
convertRequestToCurlCommand,
|
||||
getHeaderValue,
|
||||
decodeBody,
|
||||
} from './utils.tsx';
|
||||
import RequestDetails from './RequestDetails.js';
|
||||
import {Request, RequestId, Response} from './types';
|
||||
import {convertRequestToCurlCommand, getHeaderValue, decodeBody} from './utils';
|
||||
import RequestDetails from './RequestDetails';
|
||||
import {clipboard} from 'electron';
|
||||
import {URL} from 'url';
|
||||
import type {Notification} from '../../plugin.tsx';
|
||||
import {DefaultKeyboardAction} from 'src/MenuBar';
|
||||
|
||||
type PersistedState = {|
|
||||
requests: {[id: RequestId]: Request},
|
||||
responses: {[id: RequestId]: Response},
|
||||
|};
|
||||
type PersistedState = {
|
||||
requests: Map<RequestId, Request>;
|
||||
responses: Map<RequestId, Response>;
|
||||
};
|
||||
|
||||
type State = {|
|
||||
selectedIds: Array<RequestId>,
|
||||
|};
|
||||
type State = {
|
||||
selectedIds: Array<RequestId>;
|
||||
};
|
||||
|
||||
const COLUMN_SIZE = {
|
||||
requestTimestamp: 100,
|
||||
@@ -67,27 +60,13 @@ const COLUMN_ORDER = [
|
||||
];
|
||||
|
||||
const COLUMNS = {
|
||||
requestTimestamp: {
|
||||
value: 'Request Time',
|
||||
},
|
||||
responseTimestamp: {
|
||||
value: 'Response Time',
|
||||
},
|
||||
domain: {
|
||||
value: 'Domain',
|
||||
},
|
||||
method: {
|
||||
value: 'Method',
|
||||
},
|
||||
status: {
|
||||
value: 'Status',
|
||||
},
|
||||
size: {
|
||||
value: 'Size',
|
||||
},
|
||||
duration: {
|
||||
value: 'Duration',
|
||||
},
|
||||
requestTimestamp: {value: 'Request Time'},
|
||||
responseTimestamp: {value: 'Response Time'},
|
||||
domain: {value: 'Domain'},
|
||||
method: {value: 'Method'},
|
||||
status: {value: 'Status'},
|
||||
size: {value: 'Size'},
|
||||
duration: {value: 'Duration'},
|
||||
};
|
||||
|
||||
export function formatBytes(count: number): string {
|
||||
@@ -108,66 +87,75 @@ const TextEllipsis = styled(Text)({
|
||||
paddingTop: 4,
|
||||
});
|
||||
|
||||
export default class extends FlipperPlugin<State, *, PersistedState> {
|
||||
static keyboardActions = ['clear'];
|
||||
export default class extends FlipperPlugin<State, any, PersistedState> {
|
||||
static keyboardActions: Array<DefaultKeyboardAction> = ['clear'];
|
||||
static subscribed = [];
|
||||
static defaultPersistedState = {
|
||||
requests: {},
|
||||
responses: {},
|
||||
requests: new Map(),
|
||||
responses: new Map(),
|
||||
};
|
||||
|
||||
static metricsReducer = (
|
||||
persistedState: PersistedState,
|
||||
): Promise<MetricType> => {
|
||||
const failures = Object.keys(persistedState.responses).reduce(function(
|
||||
static metricsReducer(persistedState: PersistedState) {
|
||||
const failures = Object.values(persistedState.responses).reduce(function(
|
||||
previous,
|
||||
key,
|
||||
values,
|
||||
) {
|
||||
return previous + (persistedState.responses[key].status >= 400);
|
||||
return previous + (values.status >= 400 ? 1 : 0);
|
||||
},
|
||||
0);
|
||||
return Promise.resolve({NUMBER_NETWORK_FAILURES: failures});
|
||||
};
|
||||
}
|
||||
|
||||
static persistedStateReducer = (
|
||||
static persistedStateReducer(
|
||||
persistedState: PersistedState,
|
||||
method: string,
|
||||
data: Request | Response,
|
||||
): PersistedState => {
|
||||
const dataType: 'requests' | 'responses' = data.url
|
||||
? 'requests'
|
||||
: 'responses';
|
||||
return {
|
||||
...persistedState,
|
||||
[dataType]: {
|
||||
...persistedState[dataType],
|
||||
[data.id]: data,
|
||||
},
|
||||
};
|
||||
};
|
||||
) {
|
||||
switch (method) {
|
||||
case 'newRequest':
|
||||
return Object.assign({}, persistedState, {
|
||||
requests: new Map(persistedState.requests).set(
|
||||
data.id,
|
||||
data as Request,
|
||||
),
|
||||
});
|
||||
case 'newResponse':
|
||||
return Object.assign({}, persistedState, {
|
||||
responses: new Map(persistedState.responses).set(
|
||||
data.id,
|
||||
data as Response,
|
||||
),
|
||||
});
|
||||
default:
|
||||
return persistedState;
|
||||
}
|
||||
}
|
||||
|
||||
static getActiveNotifications = (
|
||||
persistedState: PersistedState,
|
||||
): Array<Notification> => {
|
||||
const responses = persistedState ? persistedState.responses || [] : [];
|
||||
const r: Array<Response> = Object.values(responses);
|
||||
static getActiveNotifications(persistedState: PersistedState) {
|
||||
const responses = persistedState
|
||||
? persistedState.responses || new Map()
|
||||
: new Map();
|
||||
const r: Array<Response> = Array.from(responses.values());
|
||||
|
||||
return (
|
||||
r
|
||||
// Show error messages for all status codes indicating a client or server error
|
||||
.filter((response: Response) => response.status >= 400)
|
||||
.map((response: Response) => ({
|
||||
id: response.id,
|
||||
title: `HTTP ${response.status}: Network request failed`,
|
||||
message: `Request to "${persistedState.requests[response.id]?.url ||
|
||||
'(URL missing)'}" failed. ${response.reason}`,
|
||||
severity: 'error',
|
||||
timestamp: response.timestamp,
|
||||
category: `HTTP${response.status}`,
|
||||
action: response.id,
|
||||
}))
|
||||
.map((response: Response) => {
|
||||
const request = persistedState.requests.get(response.id);
|
||||
const url: string = (request && request.url) || '(URL missing)';
|
||||
return {
|
||||
id: response.id,
|
||||
title: `HTTP ${response.status}: Network request failed`,
|
||||
message: `Request to ${url} failed. ${response.reason}`,
|
||||
severity: 'error' as 'error',
|
||||
timestamp: response.timestamp,
|
||||
category: `HTTP${response.status}`,
|
||||
action: response.id,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onKeyboardAction = (action: string) => {
|
||||
if (action === 'clear') {
|
||||
@@ -190,14 +178,17 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
||||
return;
|
||||
}
|
||||
|
||||
const request = requests[selectedIds[0]];
|
||||
const request = requests.get(selectedIds[0]);
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
const command = convertRequestToCurlCommand(request);
|
||||
clipboard.writeText(command);
|
||||
};
|
||||
|
||||
clearLogs = () => {
|
||||
this.setState({selectedIds: []});
|
||||
this.props.setPersistedState({responses: {}, requests: {}});
|
||||
this.props.setPersistedState({responses: new Map(), requests: new Map()});
|
||||
};
|
||||
|
||||
renderSidebar = () => {
|
||||
@@ -205,13 +196,20 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
||||
const {selectedIds} = this.state;
|
||||
const selectedId = selectedIds.length === 1 ? selectedIds[0] : null;
|
||||
|
||||
return selectedId != null ? (
|
||||
if (!selectedId) {
|
||||
return null;
|
||||
}
|
||||
const requestWithId = requests.get(selectedId);
|
||||
if (!requestWithId) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<RequestDetails
|
||||
key={selectedId}
|
||||
request={requests[selectedId]}
|
||||
response={responses[selectedId]}
|
||||
request={requestWithId}
|
||||
response={responses.get(selectedId)}
|
||||
/>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -220,8 +218,8 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
||||
return (
|
||||
<FlexColumn grow={true}>
|
||||
<NetworkTable
|
||||
requests={requests || {}}
|
||||
responses={responses || {}}
|
||||
requests={requests || new Map()}
|
||||
responses={responses || new Map()}
|
||||
clear={this.clearLogs}
|
||||
copyRequestCurlCommand={this.copyRequestCurlCommand}
|
||||
onRowHighlighted={this.onRowHighlighted}
|
||||
@@ -236,17 +234,17 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
||||
}
|
||||
|
||||
type NetworkTableProps = {
|
||||
requests: {[id: RequestId]: Request},
|
||||
responses: {[id: RequestId]: Response},
|
||||
clear: () => void,
|
||||
copyRequestCurlCommand: () => void,
|
||||
onRowHighlighted: (keys: TableHighlightedRows) => void,
|
||||
highlightedRows: ?Set<string>,
|
||||
requests: Map<RequestId, Request>;
|
||||
responses: Map<RequestId, Response>;
|
||||
clear: () => void;
|
||||
copyRequestCurlCommand: () => void;
|
||||
onRowHighlighted: (keys: TableHighlightedRows) => void;
|
||||
highlightedRows: Set<string> | null | undefined;
|
||||
};
|
||||
|
||||
type NetworkTableState = {|
|
||||
sortedRows: TableRows,
|
||||
|};
|
||||
type NetworkTableState = {
|
||||
sortedRows: TableRows;
|
||||
};
|
||||
|
||||
function formatTimestamp(timestamp: number): string {
|
||||
const date = new Date(timestamp);
|
||||
@@ -261,9 +259,12 @@ function formatTimestamp(timestamp: number): string {
|
||||
)}`;
|
||||
}
|
||||
|
||||
function buildRow(request: Request, response: ?Response): ?TableBodyRow {
|
||||
function buildRow(
|
||||
request: Request,
|
||||
response: Response | null | undefined,
|
||||
): TableBodyRow | null | undefined {
|
||||
if (request == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const url = new URL(request.url);
|
||||
const domain = url.host + url.pathname;
|
||||
@@ -273,7 +274,10 @@ function buildRow(request: Request, response: ?Response): ?TableBodyRow {
|
||||
## Request
|
||||
HTTP ${request.method} ${request.url}
|
||||
${request.headers
|
||||
.map(({key, value}) => `${key}: ${String(value)}`)
|
||||
.map(
|
||||
({key, value}: {key: string; value: string}): string =>
|
||||
`${key}: ${String(value)}`,
|
||||
)
|
||||
.join('\n')}`;
|
||||
|
||||
if (request.data) {
|
||||
@@ -286,7 +290,10 @@ ${request.headers
|
||||
## Response
|
||||
HTTP ${response.status} ${response.reason}
|
||||
${response.headers
|
||||
.map(({key, value}) => `${key}: ${String(value)}`)
|
||||
.map(
|
||||
({key, value}: {key: string; value: string}): string =>
|
||||
`${key}: ${String(value)}`,
|
||||
)
|
||||
.join('\n')}`;
|
||||
}
|
||||
|
||||
@@ -341,50 +348,49 @@ ${response.headers
|
||||
|
||||
function calculateState(
|
||||
props: {
|
||||
requests: {[id: RequestId]: Request},
|
||||
responses: {[id: RequestId]: Response},
|
||||
requests: Map<RequestId, Request>;
|
||||
responses: Map<RequestId, Response>;
|
||||
},
|
||||
nextProps: NetworkTableProps,
|
||||
rows: TableRows = [],
|
||||
): NetworkTableState {
|
||||
rows = [...rows];
|
||||
|
||||
if (Object.keys(nextProps.requests).length === 0) {
|
||||
// if (nextProps.requests.size === undefined || nextProps.requests.size === 0) {
|
||||
if (nextProps.requests.size === 0) {
|
||||
// cleared
|
||||
rows = [];
|
||||
} else if (props.requests !== nextProps.requests) {
|
||||
// new request
|
||||
for (const requestId in nextProps.requests) {
|
||||
if (props.requests[requestId] == null) {
|
||||
const newRow = buildRow(
|
||||
nextProps.requests[requestId],
|
||||
nextProps.responses[requestId],
|
||||
);
|
||||
nextProps.requests.forEach((request: Request, requestId: RequestId) => {
|
||||
if (props.requests.get(requestId) == null) {
|
||||
const newRow = buildRow(request, nextProps.responses.get(requestId));
|
||||
if (newRow) {
|
||||
rows.push(newRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (props.responses !== nextProps.responses) {
|
||||
// new response
|
||||
for (const responseId in nextProps.responses) {
|
||||
if (props.responses[responseId] == null) {
|
||||
const newRow = buildRow(
|
||||
nextProps.requests[responseId],
|
||||
nextProps.responses[responseId],
|
||||
);
|
||||
const index = rows.findIndex(
|
||||
r => r.key === nextProps.requests[responseId]?.id,
|
||||
);
|
||||
const resId = Array.from(nextProps.responses.keys()).find(
|
||||
(responseId: RequestId) => !props.responses.get(responseId),
|
||||
);
|
||||
if (resId) {
|
||||
const request = nextProps.requests.get(resId);
|
||||
// sanity check; to pass null check
|
||||
if (request) {
|
||||
const newRow = buildRow(request, nextProps.responses.get(resId));
|
||||
const index = rows.findIndex((r: TableBodyRow) => r.key === request.id);
|
||||
if (index > -1 && newRow) {
|
||||
rows[index] = newRow;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rows.sort((a, b) => Number(a.sortKey) - Number(b.sortKey));
|
||||
rows.sort(
|
||||
(a: TableBodyRow, b: TableBodyRow) => Number(a.sortKey) - Number(b.sortKey),
|
||||
);
|
||||
|
||||
return {
|
||||
sortedRows: rows,
|
||||
@@ -400,8 +406,8 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
super(props);
|
||||
this.state = calculateState(
|
||||
{
|
||||
requests: {},
|
||||
responses: {},
|
||||
requests: new Map(),
|
||||
responses: new Map(),
|
||||
},
|
||||
props,
|
||||
);
|
||||
@@ -411,13 +417,20 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
this.setState(calculateState(this.props, nextProps, this.state.sortedRows));
|
||||
}
|
||||
|
||||
contextMenuItems() {
|
||||
contextMenuItems(): Array<MenuItemConstructorOptions> {
|
||||
type ContextMenuType =
|
||||
| 'normal'
|
||||
| 'separator'
|
||||
| 'submenu'
|
||||
| 'checkbox'
|
||||
| 'radio';
|
||||
const separator: ContextMenuType = 'separator';
|
||||
const {clear, copyRequestCurlCommand, highlightedRows} = this.props;
|
||||
const highlightedMenuItems =
|
||||
highlightedRows && highlightedRows.size === 1
|
||||
? [
|
||||
{
|
||||
type: 'separator',
|
||||
type: separator,
|
||||
},
|
||||
{
|
||||
label: 'Copy as cURL',
|
||||
@@ -428,7 +441,7 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
|
||||
return highlightedMenuItems.concat([
|
||||
{
|
||||
type: 'separator',
|
||||
type: separator,
|
||||
},
|
||||
{
|
||||
label: 'Clear all',
|
||||
@@ -439,7 +452,9 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<NetworkTable.ContextMenu items={this.contextMenuItems()}>
|
||||
<NetworkTable.ContextMenu
|
||||
items={this.contextMenuItems()}
|
||||
component={FlexColumn}>
|
||||
<SearchableTable
|
||||
virtual={true}
|
||||
multiline={false}
|
||||
@@ -468,7 +483,7 @@ const Icon = styled(Glyph)({
|
||||
});
|
||||
|
||||
class StatusColumn extends PureComponent<{
|
||||
children?: number,
|
||||
children?: number;
|
||||
}> {
|
||||
render() {
|
||||
const {children} = this.props;
|
||||
@@ -488,8 +503,8 @@ class StatusColumn extends PureComponent<{
|
||||
}
|
||||
|
||||
class DurationColumn extends PureComponent<{
|
||||
request: Request,
|
||||
response: ?Response,
|
||||
request: Request;
|
||||
response: Response | null | undefined;
|
||||
}> {
|
||||
static Text = styled(Text)({
|
||||
flex: 1,
|
||||
@@ -511,7 +526,7 @@ class DurationColumn extends PureComponent<{
|
||||
}
|
||||
|
||||
class SizeColumn extends PureComponent<{
|
||||
response: ?Response,
|
||||
response: Response | null | undefined;
|
||||
}> {
|
||||
static Text = styled(Text)({
|
||||
flex: 1,
|
||||
@@ -529,7 +544,11 @@ class SizeColumn extends PureComponent<{
|
||||
}
|
||||
}
|
||||
|
||||
getResponseLength(response) {
|
||||
getResponseLength(response: Response | null | undefined) {
|
||||
if (!response) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let length = 0;
|
||||
const lengthString = response.headers
|
||||
? getHeaderValue(response.headers, 'content-length')
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Network",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"main": "index.tsx",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.6",
|
||||
|
||||
10
types/XmlBeautifier.d.tsx
Normal file
10
types/XmlBeautifier.d.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
declare module 'xml-beautifier' {
|
||||
export default function(xml: string, indent?: string): string;
|
||||
}
|
||||
Reference in New Issue
Block a user