Wrap plugin calls into isConnected guards
Summary: Previously, plugins could relatively safely use `client.call` at any moment in time to fetch some information from / to the client. Except for some raise conditions there was generally speaking a connection available. With this stack it becomes possible to interact with plugins even after an app (unexpectedly) disconnected, which makes Flipper a lot more versatile, especially when it comes to inspect crashes post mortem. (For more explanation see the second diff in this stack) However, this means that it is no longer safe to assume there is always a connection available. For that reason `client.isConnected` has been introduced to safeguard against that. This diff introduces guards on all user interactions that are not super explicitly triggered by the user to avoid a lot of errors being generated. This is mostly in `init()` blocks and implicit user events like selecting or hovering rows. Explicit user interactions like pressing buttons are not guarded by this diff, as ideally failure to communicate with the client should be communicated back to the user more explicitly rather than failing silently. The next diff will introduce a fallback mechanism where a popup is shown in case those calls aren't guarded. Fixed a few key warnings while at it. Reviewed By: nikoant Differential Revision: D26275604 fbshipit-source-id: 5630066cdd9541e448a6dd1f8a21861b5d751ced
This commit is contained in:
committed by
Facebook GitHub Bot
parent
43c68c0e7c
commit
60cfcb54a1
@@ -354,6 +354,10 @@ export default function createTableNativePlugin(id: string, title: string) {
|
||||
|
||||
getTableMetadata = () => {
|
||||
if (!this.props.persistedState.tableMetadata) {
|
||||
if (!this.client.isConnected) {
|
||||
this.setState({error: 'Application disconnected'});
|
||||
return;
|
||||
}
|
||||
this.client
|
||||
.call('getMetadata')
|
||||
.then((metadata) => {
|
||||
|
||||
@@ -150,19 +150,7 @@ function renderStatusMessage(
|
||||
isAppConnected: boolean,
|
||||
): React.ReactNode {
|
||||
if (!activeDevice) {
|
||||
return (
|
||||
<Layout.Horizontal gap center>
|
||||
<ExclamationCircleOutlined style={{color: theme.warningColor}} />
|
||||
<Text
|
||||
type="secondary"
|
||||
style={{
|
||||
textTransform: 'uppercase',
|
||||
fontSize: '0.8em',
|
||||
}}>
|
||||
Device disconnected
|
||||
</Text>
|
||||
</Layout.Horizontal>
|
||||
);
|
||||
return;
|
||||
}
|
||||
return !isDeviceConnected ? (
|
||||
activeDevice.isArchived ? (
|
||||
|
||||
@@ -108,11 +108,15 @@ export class InspectorSidebar extends Component<Props, State> {
|
||||
}
|
||||
|
||||
checkIfConsoleIsEnabled() {
|
||||
this.props.client
|
||||
.call('isConsoleEnabled')
|
||||
.then((result: {isEnabled: boolean}) => {
|
||||
this.setState({isConsoleEnabled: result.isEnabled});
|
||||
});
|
||||
if (this.props.client.isConnected) {
|
||||
this.props.client
|
||||
.call('isConsoleEnabled')
|
||||
.then((result: {isEnabled: boolean}) => {
|
||||
this.setState({isConsoleEnabled: result.isEnabled});
|
||||
});
|
||||
} else {
|
||||
this.setState({isConsoleEnabled: false});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -879,19 +879,21 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
||||
}
|
||||
|
||||
init() {
|
||||
this.databaseClient = new DatabaseClient(this.client);
|
||||
this.databaseClient.getDatabases({}).then((databases) => {
|
||||
this.dispatchAction({
|
||||
type: 'UpdateDatabases',
|
||||
databases,
|
||||
if (this.client.isConnected) {
|
||||
this.databaseClient = new DatabaseClient(this.client);
|
||||
this.databaseClient.getDatabases({}).then((databases) => {
|
||||
this.dispatchAction({
|
||||
type: 'UpdateDatabases',
|
||||
databases,
|
||||
});
|
||||
});
|
||||
});
|
||||
this.dispatchAction({
|
||||
type: 'UpdateFavorites',
|
||||
favorites: JSON.parse(
|
||||
localStorage.getItem('plugin-database-favorites-sql-queries') || '[]',
|
||||
),
|
||||
});
|
||||
this.dispatchAction({
|
||||
type: 'UpdateFavorites',
|
||||
favorites: JSON.parse(
|
||||
localStorage.getItem('plugin-database-favorites-sql-queries') || '[]',
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDataClicked = () => {
|
||||
|
||||
@@ -87,13 +87,15 @@ export default class Example extends FlipperPlugin<State, any, PersistedState> {
|
||||
* Call a method of the mobile counterpart, to display a message.
|
||||
*/
|
||||
sendMessage() {
|
||||
this.client
|
||||
.call('displayMessage', {message: this.state.message || 'Weeeee!'})
|
||||
.then((_params: DisplayMessageResponse) => {
|
||||
this.setState({
|
||||
prompt: 'Nice',
|
||||
if (this.client.isConnected) {
|
||||
this.client
|
||||
.call('displayMessage', {message: this.state.message || 'Weeeee!'})
|
||||
.then((_params: DisplayMessageResponse) => {
|
||||
this.setState({
|
||||
prompt: 'Nice',
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -284,13 +284,17 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
|
||||
init() {
|
||||
debugLog('init()');
|
||||
this.updateCaches('init');
|
||||
this.client.subscribe(
|
||||
'debug_overlay_event',
|
||||
(event: FrescoDebugOverlayEvent) => {
|
||||
this.setState({isDebugOverlayEnabled: event.enabled});
|
||||
},
|
||||
);
|
||||
if (this.client.isConnected) {
|
||||
this.updateCaches('init');
|
||||
this.client.subscribe(
|
||||
'debug_overlay_event',
|
||||
(event: FrescoDebugOverlayEvent) => {
|
||||
this.setState({isDebugOverlayEnabled: event.enabled});
|
||||
},
|
||||
);
|
||||
} else {
|
||||
debugLog(`not connected)`);
|
||||
}
|
||||
this.imagePool = new ImagePool(this.getImage, (images: ImagesMap) =>
|
||||
this.props.setPersistedState({imagesMap: images}),
|
||||
);
|
||||
@@ -378,6 +382,10 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
};
|
||||
|
||||
getImage = (imageId: string) => {
|
||||
if (!this.client.isConnected) {
|
||||
debugLog(`Cannot fetch image ${imageId}: disconnected`);
|
||||
return;
|
||||
}
|
||||
debugLog('<- getImage requested for ' + imageId);
|
||||
this.client.call('getImage', {imageId}).then((image: ImageData) => {
|
||||
debugLog('-> getImage ' + imageId + ' returned');
|
||||
|
||||
@@ -113,13 +113,18 @@ export default class Inspector extends Component<Props, State> {
|
||||
{
|
||||
label: 'Focus',
|
||||
click: (id: ElementID) => {
|
||||
this.props.client.call('onRequestAXFocus', {id});
|
||||
if (this.props.client.isConnected) {
|
||||
this.props.client.call('onRequestAXFocus', {id});
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.client.isConnected) {
|
||||
return;
|
||||
}
|
||||
this.props.client.call(this.call().GET_ROOT).then((root: Element) => {
|
||||
this.props.setPersistedState({
|
||||
[this.props.ax ? 'rootAXElement' : 'rootElement']: root.id,
|
||||
@@ -309,7 +314,7 @@ export default class Inspector extends Component<Props, State> {
|
||||
ids: Array<ElementID> = [],
|
||||
options: GetNodesOptions,
|
||||
): Promise<Array<Element>> {
|
||||
if (ids.length > 0) {
|
||||
if (ids.length > 0 && this.props.client.isConnected) {
|
||||
const {forAccessibilityEvent} = options;
|
||||
const {
|
||||
elements,
|
||||
@@ -404,6 +409,9 @@ export default class Inspector extends Component<Props, State> {
|
||||
});
|
||||
|
||||
onElementHovered = debounce((key: ElementID | null | undefined) => {
|
||||
if (!this.props.client.isConnected) {
|
||||
return;
|
||||
}
|
||||
this.props.client.call(this.call().SET_HIGHLIGHTED, {
|
||||
id: key,
|
||||
isAlignmentMode: this.props.inAlignmentMode,
|
||||
|
||||
@@ -320,8 +320,8 @@ export default class LayoutPlugin extends FlipperPlugin<
|
||||
: this.client;
|
||||
}
|
||||
onToggleAlignmentMode = () => {
|
||||
if (this.client.isConnected) {
|
||||
if (this.state.selectedElement) {
|
||||
if (this.state.selectedElement) {
|
||||
if (this.client.isConnected) {
|
||||
this.client.send('setHighlighted', {
|
||||
id: this.state.selectedElement,
|
||||
inAlignmentMode: !this.state.inAlignmentMode,
|
||||
|
||||
@@ -72,6 +72,9 @@ export default class SandboxView extends FlipperPlugin<
|
||||
});
|
||||
|
||||
init() {
|
||||
if (!this.client.isConnected) {
|
||||
return;
|
||||
}
|
||||
this.client.call('getSandbox', {}).then((results: Array<Sandbox>) => {
|
||||
this.setState({sandboxes: results});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user