Skip to content

Commit d12d075

Browse files
Siqi LiuFacebook Github Bot 7
authored andcommitted
Add detail view for network inspector
Summary: This diff adds a detail view for the network inspector. When pressing one item in the network flow list, a popup scrollView with detailed information about the network request will be shown. More interesting, the detail information is shown in real time, which means the detail information will be updated dynamically as soon as the network request is updated (maybe receiving a response after waiting). Also have made sure this works on both Android and iOS. Reviewed By: davidaurelio Differential Revision: D3627566 fbshipit-source-id: e868d0c0287d392018b9fa64fce53b4c4b3d76d9
1 parent ecea0ce commit d12d075

File tree

1 file changed

+156
-25
lines changed

1 file changed

+156
-25
lines changed

Libraries/Inspector/NetworkOverlay.js

Lines changed: 156 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
const ListView = require('ListView');
1515
const React = require('React');
1616
const RecyclerViewBackedScrollView = require('RecyclerViewBackedScrollView');
17+
const ScrollView = require('ScrollView');
1718
const StyleSheet = require('StyleSheet');
1819
const Text = require('Text');
1920
const TouchableHighlight = require('TouchableHighlight');
@@ -46,35 +47,46 @@ class NetworkOverlay extends React.Component {
4647
_listViewDataSource: ListView.DataSource;
4748
_listView: ?ListView;
4849
_listViewHighlighted: bool;
49-
_listViewHeight: ?number;
50+
_listViewHeight: number;
51+
_scrollView: ?ScrollView;
52+
_detailViewItems: Array<Array<ReactElement<any>>>;
5053
_listViewOnLayout: (event: Event) => void;
51-
_captureRequestList: (listRef: ?ListView) => void;
54+
_captureRequestListView: (listRef: ?ListView) => void;
55+
_captureDetailScrollView: (scrollRef: ?ScrollView) => void;
5256
_renderRow: (
5357
rowData: NetworkRequestInfo,
5458
sectionID: number,
5559
rowID: number,
5660
highlightRow: (sectionID: number, rowID: number) => void,
5761
) => ReactElement<any>;
5862
_renderScrollComponent: (props: Object) => ReactElement<any>;
63+
_closeButtonClicked: () => void;
5964

6065
state: {
6166
dataSource: ListView.DataSource,
67+
newDetailInfo: bool,
68+
detailRowID: ?number,
6269
};
6370

6471
constructor(props: Object) {
6572
super(props);
6673
this._requests = [];
74+
this._detailViewItems = [];
6775
this._listViewDataSource =
6876
new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
6977
this.state = {
7078
dataSource: this._listViewDataSource.cloneWithRows([]),
79+
newDetailInfo: false,
80+
detailRowID: null,
7181
};
7282
this._listViewHighlighted = false;
73-
this._listViewHeight = null;
74-
this._captureRequestList = this._captureRequestList.bind(this);
83+
this._listViewHeight = 0;
84+
this._captureRequestListView = this._captureRequestListView.bind(this);
85+
this._captureDetailScrollView = this._captureDetailScrollView.bind(this);
7586
this._listViewOnLayout = this._listViewOnLayout.bind(this);
7687
this._renderRow = this._renderRow.bind(this);
7788
this._renderScrollComponent = this._renderScrollComponent.bind(this);
89+
this._closeButtonClicked = this._closeButtonClicked.bind(this);
7890
}
7991

8092
_enableInterception(): void {
@@ -87,29 +99,36 @@ class NetworkOverlay extends React.Component {
8799
// so that we can distinguish different xhr objects in callbacks.
88100
xhr._index = this._requests.length;
89101
const _xhr: NetworkRequestInfo = {'method': method, 'url': url};
90-
this._requests = this._requests.concat(_xhr);
102+
this._requests.push(_xhr);
103+
this._detailViewItems.push([]);
104+
this._genDetailViewItem(xhr._index);
91105
this.setState(
92106
{dataSource: this._listViewDataSource.cloneWithRows(this._requests)},
93107
this._scrollToBottom(),
94108
);
95109
}.bind(this));
96110

97111
XHRInterceptor.setRequestHeaderCallback(function(header, value, xhr) {
98-
if (!this._requests[xhr._index].requestHeaders) {
99-
this._requests[xhr._index].requestHeaders = {};
112+
const networkInfo = this._requests[xhr._index];
113+
if (!networkInfo.requestHeaders) {
114+
networkInfo.requestHeaders = {};
100115
}
101-
this._requests[xhr._index].requestHeaders[header] = value;
116+
networkInfo.requestHeaders[header] = value;
117+
this._genDetailViewItem(xhr._index);
102118
}.bind(this));
103119

104120
XHRInterceptor.setSendCallback(function(data, xhr) {
105121
this._requests[xhr._index].dataSent = data;
122+
this._genDetailViewItem(xhr._index);
106123
}.bind(this));
107124

108125
XHRInterceptor.setHeaderReceivedCallback(
109126
function(type, size, responseHeaders, xhr) {
110-
this._requests[xhr._index].responseContentType = type;
111-
this._requests[xhr._index].responseSize = size;
112-
this._requests[xhr._index].responseHeaders = responseHeaders;
127+
const networkInfo = this._requests[xhr._index];
128+
networkInfo.responseContentType = type;
129+
networkInfo.responseSize = size;
130+
networkInfo.responseHeaders = responseHeaders;
131+
this._genDetailViewItem(xhr._index);
113132
}.bind(this)
114133
);
115134

@@ -122,11 +141,13 @@ class NetworkOverlay extends React.Component {
122141
responseType,
123142
xhr,
124143
) {
125-
this._requests[xhr._index].status = status;
126-
this._requests[xhr._index].timeout = timeout;
127-
this._requests[xhr._index].response = response;
128-
this._requests[xhr._index].responseURL = responseURL;
129-
this._requests[xhr._index].responseType = responseType;
144+
const networkInfo = this._requests[xhr._index];
145+
networkInfo.status = status;
146+
networkInfo.timeout = timeout;
147+
networkInfo.response = response;
148+
networkInfo.responseURL = responseURL;
149+
networkInfo.responseType = responseType;
150+
this._genDetailViewItem(xhr._index);
130151
}.bind(this)
131152
);
132153

@@ -193,7 +214,7 @@ class NetworkOverlay extends React.Component {
193214
}
194215

195216
_scrollToBottom(): void {
196-
if (!!this._listView && !!this._listViewHeight) {
217+
if (this._listView) {
197218
const scrollResponder = this._listView.getScrollResponder();
198219
if (scrollResponder) {
199220
const scrollY = Math.max(
@@ -211,7 +232,7 @@ class NetworkOverlay extends React.Component {
211232
}
212233
}
213234

214-
_captureRequestList(listRef: ?ListView): void {
235+
_captureRequestListView(listRef: ?ListView): void {
215236
this._listView = listRef;
216237
}
217238

@@ -227,29 +248,106 @@ class NetworkOverlay extends React.Component {
227248
}
228249

229250
/**
230-
* TODO: When item is pressed, should pop up another view to show details.
251+
* Popup a scrollView to dynamically show detailed information of
252+
* the request, when pressing a row in the network flow listView.
231253
*/
232254
_pressRow(rowID: number): void {
233255
this._listViewHighlighted = true;
256+
this.setState(
257+
{detailRowID: rowID},
258+
this._scrollToTop(),
259+
);
260+
}
261+
262+
_scrollToTop(): void {
263+
if (this._scrollView) {
264+
this._scrollView.scrollTo({
265+
y: 0,
266+
animated: false,
267+
});
268+
}
269+
}
270+
271+
_captureDetailScrollView(scrollRef: ?ScrollView): void {
272+
this._scrollView = scrollRef;
273+
}
274+
275+
_closeButtonClicked() {
276+
this.setState({detailRowID: null});
277+
}
278+
279+
_getStringByValue(value: any): string {
280+
if (value === undefined) {
281+
return 'undefined';
282+
}
283+
if (typeof value === 'object') {
284+
return JSON.stringify(value);
285+
}
286+
if (typeof value === 'string' && value.length > 500) {
287+
return String(value).substr(0, 500).concat('\n***TRUNCATED TO 500 CHARACTERS***');
288+
}
289+
return value;
290+
}
291+
292+
/**
293+
* Generate a list of views containing network request information for
294+
* a XHR object, to be shown in the detail scrollview. This function
295+
* should be called every time there is a new update of the XHR object,
296+
* in order to show network request/response information in real time.
297+
*/
298+
_genDetailViewItem(index: number): void {
299+
this._detailViewItems[index] = [];
300+
const detailViewItem = this._detailViewItems[index];
301+
const requestItem = this._requests[index];
302+
for (let key in requestItem) {
303+
detailViewItem.push(
304+
<View style={styles.detailViewRow} key={key}>
305+
<Text style={[styles.detailViewText, styles.detailKeyCellView]}>
306+
{key}
307+
</Text>
308+
<Text style={[styles.detailViewText, styles.detailValueCellView]}>
309+
{this._getStringByValue(requestItem[key])}
310+
</Text>
311+
</View>
312+
);
313+
}
314+
// Re-render if this network request is showing in the detail view.
315+
if (this.state.detailRowID != null && this.state.detailRowID == index) {
316+
this.setState({newDetailInfo: true});
317+
}
234318
}
235319

236320
render() {
237321
return (
238322
<View style={styles.container}>
239-
{this._requests.length > 0 &&
240-
<View>
323+
{this.state.detailRowID != null &&
324+
<TouchableHighlight
325+
style={styles.closeButton}
326+
onPress={this._closeButtonClicked}>
327+
<View>
328+
<Text style={styles.clostButtonText}>v</Text>
329+
</View>
330+
</TouchableHighlight>}
331+
{this.state.detailRowID != null &&
332+
<ScrollView
333+
style={styles.detailScrollView}
334+
ref={this._captureDetailScrollView}>
335+
{this._detailViewItems[this.state.detailRowID]}
336+
</ScrollView>}
337+
<View style={styles.listViewTitle}>
338+
{this._requests.length > 0 &&
241339
<View style={styles.tableRow}>
242340
<View style={styles.urlTitleCellView}>
243341
<Text style={styles.cellText} numberOfLines={1}>URL</Text>
244342
</View>
245343
<View style={styles.methodTitleCellView}>
246344
<Text style={styles.cellText} numberOfLines={1}>Method</Text>
247345
</View>
248-
</View>
249-
</View>}
346+
</View>}
347+
</View>
250348
<ListView
251349
style={styles.listView}
252-
ref={this._captureRequestList}
350+
ref={this._captureRequestListView}
253351
dataSource={this.state.dataSource}
254352
renderRow={this._renderRow}
255353
renderScrollComponent={this._renderScrollComponent}
@@ -264,14 +362,17 @@ class NetworkOverlay extends React.Component {
264362

265363
const styles = StyleSheet.create({
266364
container: {
267-
height: 100,
268365
paddingTop: 10,
269366
paddingBottom: 10,
270367
paddingLeft: 5,
271368
paddingRight: 5,
272369
},
370+
listViewTitle: {
371+
height: 20,
372+
},
273373
listView: {
274374
flex: 1,
375+
height: 60,
275376
},
276377
tableRow: {
277378
flexDirection: 'row',
@@ -342,6 +443,36 @@ const styles = StyleSheet.create({
342443
flex: 5,
343444
paddingLeft: 3,
344445
},
446+
detailScrollView: {
447+
flex: 1,
448+
height: 180,
449+
marginTop: 5,
450+
marginBottom: 5,
451+
},
452+
detailKeyCellView: {
453+
flex: 1.3,
454+
},
455+
detailValueCellView: {
456+
flex: 2,
457+
},
458+
detailViewRow: {
459+
flexDirection: 'row',
460+
paddingHorizontal: 3,
461+
},
462+
detailViewText: {
463+
color: 'white',
464+
fontSize: 11,
465+
},
466+
clostButtonText: {
467+
color: 'white',
468+
fontSize: 10,
469+
},
470+
closeButton: {
471+
backgroundColor: '#888',
472+
justifyContent: 'center',
473+
alignItems: 'center',
474+
right: 0,
475+
},
345476
});
346477

347478
module.exports = NetworkOverlay;

0 commit comments

Comments
 (0)