From ecea0cef74f9adf8b3497484bd2811f74aad4d46 Mon Sep 17 00:00:00 2001 From: Siqi Liu Date: Wed, 27 Jul 2016 12:17:07 -0700 Subject: [PATCH] Add UI to show intercepted network information in the inspector Summary: This diff adds a UI to display network information flows in the inspector tool (Android and iOS both supported): - uses a ListView to show network flows, always scrolling to the latest item when a new network request occurs. - displays the network requests as soon as they were created. - highlights the selection row and toggles events to log the detailed intercepted information. (Next diff will draw UIs for this). Follow-up: - Will add one detail view to show all valuable information about a network request, after user clicks on one item in the ListView. - Add more tabs in the detail view, ideally looking like the chrome network panel. Reviewed By: davidaurelio Differential Revision: D3598900 fbshipit-source-id: 5ec9ade6d13e3a9110db105fafbc7ba52adc515d --- Libraries/Inspector/NetworkOverlay.js | 223 ++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/Libraries/Inspector/NetworkOverlay.js b/Libraries/Inspector/NetworkOverlay.js index 6176a6444ec..9dd62a8e46c 100644 --- a/Libraries/Inspector/NetworkOverlay.js +++ b/Libraries/Inspector/NetworkOverlay.js @@ -11,11 +11,18 @@ */ 'use strict'; +const ListView = require('ListView'); const React = require('React'); +const RecyclerViewBackedScrollView = require('RecyclerViewBackedScrollView'); const StyleSheet = require('StyleSheet'); +const Text = require('Text'); +const TouchableHighlight = require('TouchableHighlight'); const View = require('View'); const XHRInterceptor = require('XHRInterceptor'); +const LISTVIEW_CELL_HEIGHT = 15; +const SEPARATOR_THICKNESS = 2; + type NetworkRequestInfo = { url?: string, method?: string, @@ -36,10 +43,38 @@ type NetworkRequestInfo = { */ class NetworkOverlay extends React.Component { _requests: Array; + _listViewDataSource: ListView.DataSource; + _listView: ?ListView; + _listViewHighlighted: bool; + _listViewHeight: ?number; + _listViewOnLayout: (event: Event) => void; + _captureRequestList: (listRef: ?ListView) => void; + _renderRow: ( + rowData: NetworkRequestInfo, + sectionID: number, + rowID: number, + highlightRow: (sectionID: number, rowID: number) => void, + ) => ReactElement; + _renderScrollComponent: (props: Object) => ReactElement; + + state: { + dataSource: ListView.DataSource, + }; constructor(props: Object) { super(props); this._requests = []; + this._listViewDataSource = + new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); + this.state = { + dataSource: this._listViewDataSource.cloneWithRows([]), + }; + this._listViewHighlighted = false; + this._listViewHeight = null; + this._captureRequestList = this._captureRequestList.bind(this); + this._listViewOnLayout = this._listViewOnLayout.bind(this); + this._renderRow = this._renderRow.bind(this); + this._renderScrollComponent = this._renderScrollComponent.bind(this); } _enableInterception(): void { @@ -53,6 +88,10 @@ class NetworkOverlay extends React.Component { xhr._index = this._requests.length; const _xhr: NetworkRequestInfo = {'method': method, 'url': url}; this._requests = this._requests.concat(_xhr); + this.setState( + {dataSource: this._listViewDataSource.cloneWithRows(this._requests)}, + this._scrollToBottom(), + ); }.bind(this)); XHRInterceptor.setRequestHeaderCallback(function(header, value, xhr) { @@ -103,9 +142,121 @@ class NetworkOverlay extends React.Component { XHRInterceptor.disableInterception(); } + _renderRow( + rowData: NetworkRequestInfo, + sectionID: number, + rowID: number, + highlightRow: (sectionID: number, rowID: number) => void, + ): ReactElement { + let urlCellViewStyle = styles.urlEvenCellView; + let methodCellViewStyle = styles.methodEvenCellView; + if (rowID % 2 === 1) { + urlCellViewStyle = styles.urlOddCellView; + methodCellViewStyle = styles.methodOddCellView; + } + return ( + { + this._pressRow(rowID); + highlightRow(sectionID, rowID); + }}> + + + + + {rowData.url} + + + + + {rowData.method} + + + + + + ); + } + + _renderSeperator( + sectionID: number, + rowID: number, + adjacentRowHighlighted: bool): ReactElement { + return ( + + ); + } + + _scrollToBottom(): void { + if (!!this._listView && !!this._listViewHeight) { + const scrollResponder = this._listView.getScrollResponder(); + if (scrollResponder) { + const scrollY = Math.max( + this._requests.length * LISTVIEW_CELL_HEIGHT + + (this._listViewHighlighted ? 2 * SEPARATOR_THICKNESS : 0) - + this._listViewHeight, + 0, + ); + scrollResponder.scrollResponderScrollTo({ + x: 0, + y: scrollY, + animated: true + }); + } + } + } + + _captureRequestList(listRef: ?ListView): void { + this._listView = listRef; + } + + _listViewOnLayout(event: any): void { + const {height} = event.nativeEvent.layout; + this._listViewHeight = height; + } + + _renderScrollComponent(props: Object): ReactElement { + return ( + + ); + } + + /** + * TODO: When item is pressed, should pop up another view to show details. + */ + _pressRow(rowID: number): void { + this._listViewHighlighted = true; + } + render() { return ( + {this._requests.length > 0 && + + + + URL + + + Method + + + } + ); } @@ -119,6 +270,78 @@ const styles = StyleSheet.create({ paddingLeft: 5, paddingRight: 5, }, + listView: { + flex: 1, + }, + tableRow: { + flexDirection: 'row', + flex: 1, + }, + cellText: { + color: 'white', + fontSize: 12, + }, + methodTitleCellView: { + height: 18, + borderColor: '#DCD7CD', + borderTopWidth: 1, + borderBottomWidth: 1, + borderRightWidth: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#444', + flex: 1, + }, + urlTitleCellView: { + height: 18, + borderColor: '#DCD7CD', + borderTopWidth: 1, + borderBottomWidth: 1, + borderLeftWidth: 1, + borderRightWidth: 1, + justifyContent: 'center', + backgroundColor: '#444', + flex: 5, + paddingLeft: 3, + }, + methodOddCellView: { + height: 15, + borderColor: '#DCD7CD', + borderRightWidth: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#000', + flex: 1, + }, + urlOddCellView: { + height: 15, + borderColor: '#DCD7CD', + borderLeftWidth: 1, + borderRightWidth: 1, + justifyContent: 'center', + backgroundColor: '#000', + flex: 5, + paddingLeft: 3, + }, + methodEvenCellView: { + height: 15, + borderColor: '#DCD7CD', + borderRightWidth: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: '#888', + flex: 1, + }, + urlEvenCellView: { + height: 15, + borderColor: '#DCD7CD', + borderLeftWidth: 1, + borderRightWidth: 1, + justifyContent: 'center', + backgroundColor: '#888', + flex: 5, + paddingLeft: 3, + }, }); module.exports = NetworkOverlay;