persist network plugin state
Summary: This saved the state of the network plugin even when switching between plugins using persistedState. A bug in the Android implementation didn't clear the events that were already sent to the desktop. Reviewed By: jknoxville Differential Revision: D8752098 fbshipit-source-id: 152ec5da83958ad8124686f780d39983cbce563f
This commit is contained in:
committed by
Facebook Github Bot
parent
f5dcaf02a4
commit
c239fcac01
@@ -49,9 +49,10 @@ public abstract class BufferingSonarPlugin implements SonarPlugin {
|
|||||||
if (mEventQueue == null) {
|
if (mEventQueue == null) {
|
||||||
mEventQueue = new RingBuffer<>(BUFFER_SIZE);
|
mEventQueue = new RingBuffer<>(BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
mEventQueue.enqueue(new CachedSonarEvent(method, sonarObject));
|
|
||||||
if (mConnection != null) {
|
if (mConnection != null) {
|
||||||
mConnection.send(method, sonarObject);
|
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()) {
|
for (CachedSonarEvent cachedSonarEvent : mEventQueue.asList()) {
|
||||||
mConnection.send(cachedSonarEvent.method, cachedSonarEvent.sonarObject);
|
mConnection.send(cachedSonarEvent.method, cachedSonarEvent.sonarObject);
|
||||||
}
|
}
|
||||||
|
mEventQueue.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ final class RingBuffer<T> {
|
|||||||
mBuffer.add(item);
|
mBuffer.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
mBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
List<T> asList() {
|
List<T> asList() {
|
||||||
return mBuffer;
|
return mBuffer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,11 @@ export class SonarBasePlugin<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SonarDevicePlugin<S = *, A = *> extends SonarBasePlugin<S, A> {
|
export class SonarDevicePlugin<S = *, A = *, P = *> extends SonarBasePlugin<
|
||||||
|
S,
|
||||||
|
A,
|
||||||
|
P,
|
||||||
|
> {
|
||||||
device: BaseDevice;
|
device: BaseDevice;
|
||||||
|
|
||||||
_setup(target: PluginTarget) {
|
_setup(target: PluginTarget) {
|
||||||
@@ -100,7 +104,7 @@ export class SonarDevicePlugin<S = *, A = *> extends SonarBasePlugin<S, A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SonarPlugin<S = *, A = *> extends SonarBasePlugin<S, A> {
|
export class SonarPlugin<S = *, A = *, P = *> extends SonarBasePlugin<S, A, P> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {TableHighlightedRows, TableRows} from 'sonar';
|
import type {TableHighlightedRows, TableRows, TableBodyRow} from 'sonar';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
@@ -17,19 +17,18 @@ import {
|
|||||||
PureComponent,
|
PureComponent,
|
||||||
SonarSidebar,
|
SonarSidebar,
|
||||||
} from 'sonar';
|
} from 'sonar';
|
||||||
|
|
||||||
import {SonarPlugin, SearchableTable} from 'sonar';
|
import {SonarPlugin, SearchableTable} from 'sonar';
|
||||||
import RequestDetails from './RequestDetails.js';
|
import RequestDetails from './RequestDetails.js';
|
||||||
|
|
||||||
import {URL} from 'url';
|
import {URL} from 'url';
|
||||||
// $FlowFixMe
|
|
||||||
import sortBy from 'lodash.sortby';
|
|
||||||
|
|
||||||
type RequestId = string;
|
type RequestId = string;
|
||||||
|
|
||||||
type State = {|
|
type PersistedState = {|
|
||||||
requests: {[id: RequestId]: Request},
|
requests: {[id: RequestId]: Request},
|
||||||
responses: {[id: RequestId]: Response},
|
responses: {[id: RequestId]: Response},
|
||||||
|
|};
|
||||||
|
|
||||||
|
type State = {|
|
||||||
selectedIds: Array<RequestId>,
|
selectedIds: Array<RequestId>,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
@@ -109,7 +108,7 @@ const TextEllipsis = Text.extends({
|
|||||||
paddingTop: 4,
|
paddingTop: 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class extends SonarPlugin<State> {
|
export default class extends SonarPlugin<State, *, PersistedState> {
|
||||||
static title = 'Network';
|
static title = 'Network';
|
||||||
static id = 'Network';
|
static id = 'Network';
|
||||||
static icon = 'internet';
|
static icon = 'internet';
|
||||||
@@ -122,53 +121,39 @@ export default class extends SonarPlugin<State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
requests: {},
|
|
||||||
responses: {},
|
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.client.subscribe('newRequest', (request: Request) => {
|
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.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<RequestId>) =>
|
onRowHighlighted = (selectedIds: Array<RequestId>) =>
|
||||||
this.setState({selectedIds});
|
this.setState({selectedIds});
|
||||||
|
|
||||||
clearLogs = () => {
|
clearLogs = () => {
|
||||||
this.setState({selectedIds: []});
|
this.setState({selectedIds: []});
|
||||||
this.dispatchAction({type: 'Clear'});
|
this.props.setPersistedState({responses: {}, requests: {}});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderSidebar = () => {
|
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;
|
const selectedId = selectedIds.length === 1 ? selectedIds[0] : null;
|
||||||
|
|
||||||
return selectedId != null ? (
|
return selectedId != null ? (
|
||||||
@@ -181,11 +166,13 @@ export default class extends SonarPlugin<State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {requests, responses} = this.props.persistedState;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlexColumn fill={true}>
|
<FlexColumn fill={true}>
|
||||||
<NetworkTable
|
<NetworkTable
|
||||||
requests={this.state.requests}
|
requests={requests || {}}
|
||||||
responses={this.state.responses}
|
responses={responses || {}}
|
||||||
clear={this.clearLogs}
|
clear={this.clearLogs}
|
||||||
onRowHighlighted={this.onRowHighlighted}
|
onRowHighlighted={this.onRowHighlighted}
|
||||||
/>
|
/>
|
||||||
@@ -195,53 +182,18 @@ export default class extends SonarPlugin<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkTableProps = {|
|
type NetworkTableProps = {
|
||||||
requests: {[id: RequestId]: Request},
|
requests: {[id: RequestId]: Request},
|
||||||
responses: {[id: RequestId]: Response},
|
responses: {[id: RequestId]: Response},
|
||||||
clear: () => void,
|
clear: () => void,
|
||||||
onRowHighlighted: (keys: TableHighlightedRows) => void,
|
onRowHighlighted: (keys: TableHighlightedRows) => void,
|
||||||
|};
|
};
|
||||||
|
|
||||||
type NetworkTableState = {|
|
type NetworkTableState = {|
|
||||||
sortedRows: TableRows,
|
sortedRows: TableRows,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
function buildRow(request: Request, response: ?Response): ?TableBodyRow {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRow(request: Request, response: ?Response) {
|
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -249,7 +201,7 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
const domain = url.host + url.pathname;
|
const domain = url.host + url.pathname;
|
||||||
const friendlyName = getHeaderValue(request.headers, 'X-FB-Friendly-Name');
|
const friendlyName = getHeaderValue(request.headers, 'X-FB-Friendly-Name');
|
||||||
|
|
||||||
const newRow = {
|
return {
|
||||||
columns: {
|
columns: {
|
||||||
domain: {
|
domain: {
|
||||||
value: (
|
value: (
|
||||||
@@ -263,9 +215,7 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
value: (
|
value: (
|
||||||
<StatusColumn>
|
<StatusColumn>{response ? response.status : undefined}</StatusColumn>
|
||||||
{response ? response.status : undefined}
|
|
||||||
</StatusColumn>
|
|
||||||
),
|
),
|
||||||
isFilterable: true,
|
isFilterable: true,
|
||||||
},
|
},
|
||||||
@@ -282,21 +232,78 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
copyText: request.url,
|
copyText: request.url,
|
||||||
highlightOnHover: true,
|
highlightOnHover: true,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let rows;
|
function calculateState(
|
||||||
if (response == null) {
|
props: {
|
||||||
rows = [...this.state.sortedRows, newRow];
|
requests: {[id: RequestId]: Request},
|
||||||
} else {
|
responses: {[id: RequestId]: Response},
|
||||||
const index = this.state.sortedRows.findIndex(r => r.key === request.id);
|
},
|
||||||
if (index > -1) {
|
nextProps: NetworkTableProps,
|
||||||
rows = [...this.state.sortedRows];
|
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;
|
rows[index] = newRow;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
rows.sort((a, b) => (String(a.sortKey) > String(b.sortKey) ? 1 : -1));
|
||||||
sortedRows: sortBy(rows, x => x.sortKey),
|
|
||||||
|
return {
|
||||||
|
sortedRows: rows,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
||||||
|
static ContextMenu = ContextMenu.extends({
|
||||||
|
flex: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
constructor(props: NetworkTableProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = calculateState(
|
||||||
|
{
|
||||||
|
requests: {},
|
||||||
|
responses: {},
|
||||||
|
},
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps: NetworkTableProps) {
|
||||||
|
this.setState(calculateState(this.props, nextProps, this.state.sortedRows));
|
||||||
}
|
}
|
||||||
|
|
||||||
contextMenuItems = [
|
contextMenuItems = [
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"pako": "^1.0.6"
|
"pako": "^1.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
# yarn lockfile v1
|
# 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:
|
pako@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
|
||||||
|
|||||||
Reference in New Issue
Block a user