Separate Client in server and client part
Summary: This diff separates the concept of a Client as now on the UI, from the concept of a Client as known on the server, and makes all interactions with client and vice versa async. Reviewed By: timur-valiev Differential Revision: D31235682 fbshipit-source-id: 99089e9b390b4c5359f97f6f2b15bf4b182b6cb9
This commit is contained in:
committed by
Facebook GitHub Bot
parent
eab4804792
commit
740093d0d9
@@ -8,13 +8,16 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {Store} from '../reducers/index';
|
||||
import {State, Store} from '../reducers/index';
|
||||
import {Logger} from '../fb-interfaces/Logger';
|
||||
import {FlipperServerImpl} from '../server/FlipperServerImpl';
|
||||
import {selectClient, selectDevice} from '../reducers/connections';
|
||||
import Client from '../Client';
|
||||
import {notification} from 'antd';
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import {ClientDescription, timeout} from 'flipper-plugin';
|
||||
import {reportPlatformFailures} from '../utils/metrics';
|
||||
import {sideEffect} from '../utils/sideEffect';
|
||||
|
||||
export default async (store: Store, logger: Logger) => {
|
||||
const {enableAndroid, androidHome, idbPath, enableIOS, enablePhysicalIOS} =
|
||||
@@ -111,11 +114,20 @@ export default async (store: Store, logger: Logger) => {
|
||||
// N.B.: note that we don't remove the device, we keep it in offline
|
||||
});
|
||||
|
||||
server.on('client-connected', (payload) =>
|
||||
// TODO: fixed later in this stack
|
||||
handleClientConnected(store, payload as any),
|
||||
server.on('client-connected', (payload: ClientDescription) =>
|
||||
handleClientConnected(server, store, logger, payload),
|
||||
);
|
||||
|
||||
server.on('client-disconnected', ({id}) => {
|
||||
const existingClient = store.getState().connections.clients.get(id);
|
||||
existingClient?.disconnect();
|
||||
});
|
||||
|
||||
server.on('client-message', ({id, message}) => {
|
||||
const existingClient = store.getState().connections.clients.get(id);
|
||||
existingClient?.onMessage(message);
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
server.close();
|
||||
@@ -142,25 +154,51 @@ export default async (store: Store, logger: Logger) => {
|
||||
};
|
||||
};
|
||||
|
||||
export async function handleClientConnected(store: Store, client: Client) {
|
||||
export async function handleClientConnected(
|
||||
server: FlipperServerImpl,
|
||||
store: Store,
|
||||
logger: Logger,
|
||||
{id, query}: ClientDescription,
|
||||
) {
|
||||
const {connections} = store.getState();
|
||||
const existingClient = connections.clients.get(client.id);
|
||||
const existingClient = connections.clients.get(id);
|
||||
|
||||
if (existingClient) {
|
||||
existingClient.destroy();
|
||||
store.dispatch({
|
||||
type: 'CLEAR_CLIENT_PLUGINS_STATE',
|
||||
payload: {
|
||||
clientId: client.id,
|
||||
clientId: id,
|
||||
devicePlugins: new Set(),
|
||||
},
|
||||
});
|
||||
store.dispatch({
|
||||
type: 'CLIENT_REMOVED',
|
||||
payload: client.id,
|
||||
payload: id,
|
||||
});
|
||||
}
|
||||
|
||||
const device =
|
||||
getDeviceBySerial(store.getState(), query.device_id) ??
|
||||
(await findDeviceForConnection(store, query.app, query.device_id));
|
||||
|
||||
const client = new Client(
|
||||
id,
|
||||
query,
|
||||
{
|
||||
send(data: any) {
|
||||
server.exec('client-request', id, data);
|
||||
},
|
||||
async sendExpectResponse(data: any) {
|
||||
return await server.exec('client-request-response', id, data);
|
||||
},
|
||||
},
|
||||
logger,
|
||||
store,
|
||||
undefined,
|
||||
device,
|
||||
);
|
||||
|
||||
console.debug(
|
||||
`Device client initialized: ${client.id}. Supported plugins: ${Array.from(
|
||||
client.plugins,
|
||||
@@ -174,5 +212,64 @@ export async function handleClientConnected(store: Store, client: Client) {
|
||||
});
|
||||
|
||||
store.dispatch(selectClient(client.id));
|
||||
client.emit('plugins-change');
|
||||
|
||||
await timeout(
|
||||
30 * 1000,
|
||||
client.init(),
|
||||
`[conn] Failed to initialize client ${query.app} on ${query.device_id} in a timely manner`,
|
||||
);
|
||||
}
|
||||
|
||||
function getDeviceBySerial(
|
||||
state: State,
|
||||
serial: string,
|
||||
): BaseDevice | undefined {
|
||||
return state.connections.devices.find((device) => device.serial === serial);
|
||||
}
|
||||
|
||||
async function findDeviceForConnection(
|
||||
store: Store,
|
||||
clientId: string,
|
||||
serial: string,
|
||||
): Promise<BaseDevice> {
|
||||
let lastSeenDeviceList: BaseDevice[] = [];
|
||||
/* All clients should have a corresponding Device in the store.
|
||||
However, clients can connect before a device is registered, so wait a
|
||||
while for the device to be registered if it isn't already. */
|
||||
return reportPlatformFailures(
|
||||
new Promise<BaseDevice>((resolve, reject) => {
|
||||
let unsubscribe: () => void = () => {};
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
unsubscribe();
|
||||
const error = `Timed out waiting for device ${serial} for client ${clientId}`;
|
||||
console.error(
|
||||
'[conn] Unable to find device for connection. Error:',
|
||||
error,
|
||||
);
|
||||
reject(error);
|
||||
}, 15000);
|
||||
unsubscribe = sideEffect(
|
||||
store,
|
||||
{name: 'waitForDevice', throttleMs: 100},
|
||||
(state) => state.connections.devices,
|
||||
(newDeviceList) => {
|
||||
if (newDeviceList === lastSeenDeviceList) {
|
||||
return;
|
||||
}
|
||||
lastSeenDeviceList = newDeviceList;
|
||||
const matchingDevice = newDeviceList.find(
|
||||
(device) => device.serial === serial,
|
||||
);
|
||||
if (matchingDevice) {
|
||||
console.log(`[conn] Found device for: ${clientId} on ${serial}.`);
|
||||
clearTimeout(timeout);
|
||||
resolve(matchingDevice);
|
||||
unsubscribe();
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
'client-setMatchingDevice',
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user