diff --git a/android/plugins/common/BufferingSonarPlugin.java b/android/plugins/common/BufferingSonarPlugin.java index 3881bddc8..48b3ea36e 100644 --- a/android/plugins/common/BufferingSonarPlugin.java +++ b/android/plugins/common/BufferingSonarPlugin.java @@ -49,9 +49,10 @@ public abstract class BufferingSonarPlugin implements SonarPlugin { if (mEventQueue == null) { mEventQueue = new RingBuffer<>(BUFFER_SIZE); } - mEventQueue.enqueue(new CachedSonarEvent(method, sonarObject)); if (mConnection != null) { mConnection.send(method, sonarObject); + } else { + mEventQueue.enqueue(new CachedSonarEvent(method, sonarObject)); } } @@ -60,6 +61,7 @@ public abstract class BufferingSonarPlugin implements SonarPlugin { for (CachedSonarEvent cachedSonarEvent : mEventQueue.asList()) { mConnection.send(cachedSonarEvent.method, cachedSonarEvent.sonarObject); } + mEventQueue.clear(); } } diff --git a/android/plugins/common/RingBuffer.java b/android/plugins/common/RingBuffer.java index 872082683..feb4470c1 100644 --- a/android/plugins/common/RingBuffer.java +++ b/android/plugins/common/RingBuffer.java @@ -25,6 +25,10 @@ final class RingBuffer { mBuffer.add(item); } + void clear() { + mBuffer.clear(); + } + List asList() { return mBuffer; } diff --git a/src/plugin.js b/src/plugin.js index 4ae2693c1..aa723ec4b 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -84,7 +84,11 @@ export class SonarBasePlugin< } } -export class SonarDevicePlugin extends SonarBasePlugin { +export class SonarDevicePlugin extends SonarBasePlugin< + S, + A, + P, +> { device: BaseDevice; _setup(target: PluginTarget) { @@ -100,7 +104,7 @@ export class SonarDevicePlugin extends SonarBasePlugin { } } -export class SonarPlugin extends SonarBasePlugin { +export class SonarPlugin extends SonarBasePlugin { constructor() { super(); this.subscriptions = []; diff --git a/src/plugins/network/index.js b/src/plugins/network/index.js index d34a8bcc3..2a957dd0e 100644 --- a/src/plugins/network/index.js +++ b/src/plugins/network/index.js @@ -5,7 +5,7 @@ * @format */ -import type {TableHighlightedRows, TableRows} from 'sonar'; +import type {TableHighlightedRows, TableRows, TableBodyRow} from 'sonar'; import { ContextMenu, @@ -17,19 +17,18 @@ import { PureComponent, SonarSidebar, } from 'sonar'; - import {SonarPlugin, SearchableTable} from 'sonar'; import RequestDetails from './RequestDetails.js'; - import {URL} from 'url'; -// $FlowFixMe -import sortBy from 'lodash.sortby'; type RequestId = string; -type State = {| +type PersistedState = {| requests: {[id: RequestId]: Request}, responses: {[id: RequestId]: Response}, +|}; + +type State = {| selectedIds: Array, |}; @@ -109,7 +108,7 @@ const TextEllipsis = Text.extends({ paddingTop: 4, }); -export default class extends SonarPlugin { +export default class extends SonarPlugin { static title = 'Network'; static id = 'Network'; static icon = 'internet'; @@ -122,53 +121,39 @@ export default class extends SonarPlugin { }; state = { - requests: {}, - responses: {}, selectedIds: [], }; init() { this.client.subscribe('newRequest', (request: Request) => { - this.dispatchAction({request, type: 'NewRequest'}); + this.props.setPersistedState({ + requests: { + ...this.props.persistedState.requests, + [request.id]: request, + }, + }); }); this.client.subscribe('newResponse', (response: Response) => { - this.dispatchAction({response, type: 'NewResponse'}); + this.props.setPersistedState({ + responses: { + ...this.props.persistedState.responses, + [response.id]: response, + }, + }); }); } - reducers = { - NewRequest(state: State, {request}: {request: Request}) { - return { - requests: {...state.requests, [request.id]: request}, - responses: state.responses, - }; - }, - - NewResponse(state: State, {response}: {response: Response}) { - return { - requests: state.requests, - responses: {...state.responses, [response.id]: response}, - }; - }, - - Clear(state: State) { - return { - requests: {}, - responses: {}, - }; - }, - }; - onRowHighlighted = (selectedIds: Array) => this.setState({selectedIds}); clearLogs = () => { this.setState({selectedIds: []}); - this.dispatchAction({type: 'Clear'}); + this.props.setPersistedState({responses: {}, requests: {}}); }; renderSidebar = () => { - const {selectedIds, requests, responses} = this.state; + const {requests, responses} = this.props.persistedState; + const {selectedIds} = this.state; const selectedId = selectedIds.length === 1 ? selectedIds[0] : null; return selectedId != null ? ( @@ -181,11 +166,13 @@ export default class extends SonarPlugin { }; render() { + const {requests, responses} = this.props.persistedState; + return ( @@ -195,108 +182,128 @@ export default class extends SonarPlugin { } } -type NetworkTableProps = {| +type NetworkTableProps = { requests: {[id: RequestId]: Request}, responses: {[id: RequestId]: Response}, clear: () => void, onRowHighlighted: (keys: TableHighlightedRows) => void, -|}; +}; type NetworkTableState = {| sortedRows: TableRows, |}; +function buildRow(request: Request, response: ?Response): ?TableBodyRow { + if (request == null) { + return; + } + const url = new URL(request.url); + const domain = url.host + url.pathname; + const friendlyName = getHeaderValue(request.headers, 'X-FB-Friendly-Name'); + + return { + columns: { + domain: { + value: ( + {friendlyName ? friendlyName : domain} + ), + isFilterable: true, + }, + method: { + value: {request.method}, + isFilterable: true, + }, + status: { + value: ( + {response ? response.status : undefined} + ), + isFilterable: true, + }, + size: { + value: , + }, + duration: { + value: , + }, + }, + key: request.id, + filterValue: `${request.method} ${request.url}`, + sortKey: request.timestamp, + copyText: request.url, + highlightOnHover: true, + }; +} + +function calculateState( + props: { + requests: {[id: RequestId]: Request}, + responses: {[id: RequestId]: Response}, + }, + nextProps: NetworkTableProps, + rows: TableRows = [], +): NetworkTableState { + rows = [...rows]; + + if (Object.keys(nextProps.requests).length === 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], + ); + 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, + ); + if (index > -1 && newRow) { + rows[index] = newRow; + } + break; + } + } + } + + rows.sort((a, b) => (String(a.sortKey) > String(b.sortKey) ? 1 : -1)); + + return { + sortedRows: rows, + }; +} + class NetworkTable extends PureComponent { static ContextMenu = ContextMenu.extends({ flex: 1, }); - state = { - sortedRows: [], - }; - - componentWillReceiveProps(nextProps: NetworkTableProps) { - if (Object.keys(nextProps.requests).length === 0) { - // cleared - this.setState({sortedRows: []}); - } else if (this.props.requests !== nextProps.requests) { - // new request - for (const requestId in nextProps.requests) { - if (this.props.requests[requestId] == null) { - this.buildRow(nextProps.requests[requestId], null); - break; - } - } - } else if (this.props.responses !== nextProps.responses) { - // new response - for (const responseId in nextProps.responses) { - if (this.props.responses[responseId] == null) { - this.buildRow( - nextProps.requests[responseId], - nextProps.responses[responseId], - ); - break; - } - } - } + constructor(props: NetworkTableProps) { + super(props); + this.state = calculateState( + { + requests: {}, + responses: {}, + }, + props, + ); } - buildRow(request: Request, response: ?Response) { - if (request == null) { - return; - } - const url = new URL(request.url); - const domain = url.host + url.pathname; - const friendlyName = getHeaderValue(request.headers, 'X-FB-Friendly-Name'); - - const newRow = { - columns: { - domain: { - value: ( - {friendlyName ? friendlyName : domain} - ), - isFilterable: true, - }, - method: { - value: {request.method}, - isFilterable: true, - }, - status: { - value: ( - - {response ? response.status : undefined} - - ), - isFilterable: true, - }, - size: { - value: , - }, - duration: { - value: , - }, - }, - key: request.id, - filterValue: `${request.method} ${request.url}`, - sortKey: request.timestamp, - copyText: request.url, - highlightOnHover: true, - }; - - let rows; - if (response == null) { - rows = [...this.state.sortedRows, newRow]; - } else { - const index = this.state.sortedRows.findIndex(r => r.key === request.id); - if (index > -1) { - rows = [...this.state.sortedRows]; - rows[index] = newRow; - } - } - - this.setState({ - sortedRows: sortBy(rows, x => x.sortKey), - }); + componentWillReceiveProps(nextProps: NetworkTableProps) { + this.setState(calculateState(this.props, nextProps, this.state.sortedRows)); } contextMenuItems = [ diff --git a/src/plugins/network/package.json b/src/plugins/network/package.json index eec4a9850..e326e7d9b 100644 --- a/src/plugins/network/package.json +++ b/src/plugins/network/package.json @@ -4,7 +4,6 @@ "main": "index.js", "license": "MIT", "dependencies": { - "lodash.sortby": "^4.7.0", "pako": "^1.0.6" } } diff --git a/src/plugins/network/yarn.lock b/src/plugins/network/yarn.lock index 3ff62dddc..3805f8e17 100644 --- a/src/plugins/network/yarn.lock +++ b/src/plugins/network/yarn.lock @@ -2,10 +2,6 @@ # yarn lockfile v1 -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - pako@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"