Fix bug where client plugins weren't always started
Summary: Changelog: Fixed issue where occasionally a plugin wouldn't open after starting Flipper This fixes a long standing issue where rarely Flipper wouldn't show the selected plugin. This turned out to be a raise condition, that was easy to reproduce in the Flipper browser version; if a client register before all the plugins are loaded, the plugins that are enabled for that client, but not loaded yet, will not instantiate and hence not show up. This diff fixes that Reviewed By: timur-valiev, aigoncharov Differential Revision: D32987162 fbshipit-source-id: f3179cd9b6f2e4e79d05be1f2236f63acdf50495
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bb23b36051
commit
fa67c21def
@@ -46,6 +46,7 @@ import {
|
|||||||
isFlipperMessageDebuggingEnabled,
|
isFlipperMessageDebuggingEnabled,
|
||||||
registerFlipperDebugMessage,
|
registerFlipperDebugMessage,
|
||||||
} from './chrome/FlipperMessages';
|
} from './chrome/FlipperMessages';
|
||||||
|
import {waitFor} from './utils/waitFor';
|
||||||
|
|
||||||
type Plugins = Set<string>;
|
type Plugins = Set<string>;
|
||||||
type PluginsArr = Array<string>;
|
type PluginsArr = Array<string>;
|
||||||
@@ -173,6 +174,8 @@ export default class Client extends EventEmitter {
|
|||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
await this.loadPlugins();
|
await this.loadPlugins();
|
||||||
|
// if a client arrives before all plugins are loaded, we'll have to wait
|
||||||
|
await waitFor(this.store, (state) => state.plugins.initialized);
|
||||||
// this starts all sandy enabled plugins
|
// this starts all sandy enabled plugins
|
||||||
this.plugins.forEach((pluginId) =>
|
this.plugins.forEach((pluginId) =>
|
||||||
this.startPluginIfNeeded(this.getPlugin(pluginId)),
|
this.startPluginIfNeeded(this.getPlugin(pluginId)),
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ Object {
|
|||||||
"disabledPlugins": Array [],
|
"disabledPlugins": Array [],
|
||||||
"failedPlugins": Array [],
|
"failedPlugins": Array [],
|
||||||
"gatekeepedPlugins": Array [],
|
"gatekeepedPlugins": Array [],
|
||||||
"initialized": false,
|
"initialized": true,
|
||||||
"installedPlugins": Map {},
|
"installedPlugins": Map {},
|
||||||
"loadedPlugins": Map {},
|
"loadedPlugins": Map {},
|
||||||
"marketplacePlugins": Array [],
|
"marketplacePlugins": Array [],
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import BaseDevice from '../devices/BaseDevice';
|
|||||||
import {ClientDescription, timeout} from 'flipper-common';
|
import {ClientDescription, timeout} from 'flipper-common';
|
||||||
import {reportPlatformFailures} from 'flipper-common';
|
import {reportPlatformFailures} from 'flipper-common';
|
||||||
import {sideEffect} from '../utils/sideEffect';
|
import {sideEffect} from '../utils/sideEffect';
|
||||||
|
import {waitFor} from '../utils/waitFor';
|
||||||
|
|
||||||
export function connectFlipperServerToStore(
|
export function connectFlipperServerToStore(
|
||||||
server: FlipperServer,
|
server: FlipperServer,
|
||||||
@@ -112,17 +113,16 @@ export function connectFlipperServerToStore(
|
|||||||
'Flipper server started and accepting device / client connections',
|
'Flipper server started and accepting device / client connections',
|
||||||
);
|
);
|
||||||
|
|
||||||
server
|
// this flow is spawned delibarately from this main flow
|
||||||
.exec('device-list')
|
waitFor(store, (state) => state.plugins.initialized)
|
||||||
|
.then(() => server.exec('device-list'))
|
||||||
.then((devices) => {
|
.then((devices) => {
|
||||||
// register all devices
|
// register all devices
|
||||||
devices.forEach((device) => {
|
devices.forEach((device) => {
|
||||||
handleDeviceConnected(server, store, logger, device);
|
handleDeviceConnected(server, store, logger, device);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => server.exec('client-list'))
|
||||||
return server.exec('client-list');
|
|
||||||
})
|
|
||||||
.then((clients) => {
|
.then((clients) => {
|
||||||
clients.forEach((client) => {
|
clients.forEach((client) => {
|
||||||
handleClientConnected(server, store, logger, client);
|
handleClientConnected(server, store, logger, client);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import React from 'react';
|
|||||||
import {Dialog, getFlipperLib} from 'flipper-plugin';
|
import {Dialog, getFlipperLib} from 'flipper-plugin';
|
||||||
import {isTest} from 'flipper-common';
|
import {isTest} from 'flipper-common';
|
||||||
import {getUser} from '../fb-stubs/user';
|
import {getUser} from '../fb-stubs/user';
|
||||||
import {State, Store} from '../reducers/index';
|
import {Store} from '../reducers/index';
|
||||||
import {checkForUpdate} from '../fb-stubs/checkForUpdate';
|
import {checkForUpdate} from '../fb-stubs/checkForUpdate';
|
||||||
import {getAppVersion} from '../utils/info';
|
import {getAppVersion} from '../utils/info';
|
||||||
import {UserNotSignedInError} from 'flipper-common';
|
import {UserNotSignedInError} from 'flipper-common';
|
||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
OpenPluginParams,
|
OpenPluginParams,
|
||||||
} from '../deeplinkTracking';
|
} from '../deeplinkTracking';
|
||||||
import {getRenderHostInstance} from '../RenderHost';
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
import {waitFor} from '../utils/waitFor';
|
||||||
|
|
||||||
export function parseOpenPluginParams(query: string): OpenPluginParams {
|
export function parseOpenPluginParams(query: string): OpenPluginParams {
|
||||||
// 'flipper://open-plugin?plugin-id=graphql&client=facebook&devices=android,ios&chrome=1&payload='
|
// 'flipper://open-plugin?plugin-id=graphql&client=facebook&devices=android,ios&chrome=1&payload='
|
||||||
@@ -272,21 +273,6 @@ async function waitForLogin(store: Store) {
|
|||||||
return waitFor(store, (state) => !!state.user?.id);
|
return waitFor(store, (state) => !!state.user?.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make this more reusable?
|
|
||||||
function waitFor(
|
|
||||||
store: Store,
|
|
||||||
predicate: (state: State) => boolean,
|
|
||||||
): Promise<void> {
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
const unsub = store.subscribe(() => {
|
|
||||||
if (predicate(store.getState())) {
|
|
||||||
unsub();
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function verifyFlipperIsUpToDate(title: string) {
|
async function verifyFlipperIsUpToDate(title: string) {
|
||||||
if (!isProduction() || isTest()) {
|
if (!isProduction() || isTest()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -143,7 +143,6 @@ function init(flipperServer: FlipperServer) {
|
|||||||
setLoggerInstance(logger);
|
setLoggerInstance(logger);
|
||||||
startGlobalErrorHandling();
|
startGlobalErrorHandling();
|
||||||
loadTheme(settings.darkMode);
|
loadTheme(settings.darkMode);
|
||||||
connectFlipperServerToStore(flipperServer, store, logger);
|
|
||||||
|
|
||||||
// rehydrate app state before exposing init
|
// rehydrate app state before exposing init
|
||||||
const persistor = persistStore(store, undefined, () => {
|
const persistor = persistStore(store, undefined, () => {
|
||||||
@@ -163,6 +162,8 @@ function init(flipperServer: FlipperServer) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connectFlipperServerToStore(flipperServer, store, logger);
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<AppFrame logger={logger} persistor={persistor} />,
|
<AppFrame logger={logger} persistor={persistor} />,
|
||||||
document.getElementById('root'),
|
document.getElementById('root'),
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
ClientResponseType,
|
ClientResponseType,
|
||||||
} from 'flipper-common';
|
} from 'flipper-common';
|
||||||
import {PluginDefinition} from '../plugin';
|
import {PluginDefinition} from '../plugin';
|
||||||
import {registerPlugins} from '../reducers/plugins';
|
import {pluginsInitialized, registerPlugins} from '../reducers/plugins';
|
||||||
import {getLogger} from 'flipper-common';
|
import {getLogger} from 'flipper-common';
|
||||||
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
import {initializeFlipperLibImplementation} from '../utils/flipperLibImplementation';
|
||||||
import pluginManager from '../dispatcher/pluginManager';
|
import pluginManager from '../dispatcher/pluginManager';
|
||||||
@@ -28,6 +28,7 @@ import ArchivedDevice from '../devices/ArchivedDevice';
|
|||||||
import {ClientQuery, DeviceOS} from 'flipper-common';
|
import {ClientQuery, DeviceOS} from 'flipper-common';
|
||||||
import {TestDevice} from './TestDevice';
|
import {TestDevice} from './TestDevice';
|
||||||
import {getRenderHostInstance} from '../RenderHost';
|
import {getRenderHostInstance} from '../RenderHost';
|
||||||
|
import {waitFor} from '../utils/waitFor';
|
||||||
|
|
||||||
export interface AppOptions {
|
export interface AppOptions {
|
||||||
plugins?: PluginDefinition[];
|
plugins?: PluginDefinition[];
|
||||||
@@ -95,6 +96,7 @@ export default class MockFlipper {
|
|||||||
this._logger,
|
this._logger,
|
||||||
);
|
);
|
||||||
this._store.dispatch(registerPlugins(plugins ?? []));
|
this._store.dispatch(registerPlugins(plugins ?? []));
|
||||||
|
this._store.dispatch(pluginsInitialized());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async initWithDeviceAndClient(
|
public async initWithDeviceAndClient(
|
||||||
|
|||||||
27
desktop/flipper-ui-core/src/utils/waitFor.tsx
Normal file
27
desktop/flipper-ui-core/src/utils/waitFor.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {State, Store} from '../reducers/index';
|
||||||
|
|
||||||
|
export async function waitFor(
|
||||||
|
store: Store,
|
||||||
|
predicate: (state: State) => boolean,
|
||||||
|
): Promise<void> {
|
||||||
|
if (predicate(store.getState())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
const unsub = store.subscribe(() => {
|
||||||
|
if (predicate(store.getState())) {
|
||||||
|
unsub();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user