separate action dispatch from server
Summary: This diff moves the first small pieces of getting device detection up and running to `server/`, and the wiring between FlipperServer and flipper core / redux is setting up specific events and dispatch actions from there. Reviewed By: timur-valiev Differential Revision: D30276776 fbshipit-source-id: b30b996d03c27459815bebeb97b05b5fe5d24bec
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4909296d86
commit
6175424d16
@@ -16,7 +16,7 @@ import {
|
|||||||
DevicePluginClient,
|
DevicePluginClient,
|
||||||
PluginClient,
|
PluginClient,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {registerNewClient} from '../server/server';
|
import {handleClientConnected} from '../dispatcher/flipperServer';
|
||||||
import {destroyDevice} from '../reducers/connections';
|
import {destroyDevice} from '../reducers/connections';
|
||||||
|
|
||||||
test('Devices can disconnect', async () => {
|
test('Devices can disconnect', async () => {
|
||||||
@@ -225,7 +225,7 @@ test('new clients replace old ones', async () => {
|
|||||||
expect(instance.instanceApi.disconnect).toBeCalledTimes(0);
|
expect(instance.instanceApi.disconnect).toBeCalledTimes(0);
|
||||||
|
|
||||||
const client2 = await createClient(device, 'AnotherApp', client.query, true);
|
const client2 = await createClient(device, 'AnotherApp', client.query, true);
|
||||||
registerNewClient(store, client2);
|
handleClientConnected(store, client2);
|
||||||
|
|
||||||
expect(client2.connected.get()).toBe(true);
|
expect(client2.connected.get()).toBe(true);
|
||||||
const instance2 = client2.sandyPluginStates.get(plugin.id)!;
|
const instance2 = client2.sandyPluginStates.get(plugin.id)!;
|
||||||
|
|||||||
117
desktop/app/src/dispatcher/flipperServer.tsx
Normal file
117
desktop/app/src/dispatcher/flipperServer.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* 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 React from 'react';
|
||||||
|
import {Store} from '../reducers/index';
|
||||||
|
import {Logger} from '../fb-interfaces/Logger';
|
||||||
|
import {startFlipperServer} from '../server/FlipperServer';
|
||||||
|
import {selectClient, selectDevice} from '../reducers/connections';
|
||||||
|
import Client from '../Client';
|
||||||
|
import {notification} from 'antd';
|
||||||
|
|
||||||
|
export default async (store: Store, logger: Logger) => {
|
||||||
|
const {enableAndroid} = store.getState().settingsState;
|
||||||
|
const server = await startFlipperServer(
|
||||||
|
{
|
||||||
|
enableAndroid,
|
||||||
|
},
|
||||||
|
store,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
server.on('server-start-error', (err) => {
|
||||||
|
notification.error({
|
||||||
|
message: 'Failed to start connection server',
|
||||||
|
description:
|
||||||
|
err.code === 'EADDRINUSE' ? (
|
||||||
|
<>
|
||||||
|
Couldn't start connection server. Looks like you have multiple
|
||||||
|
copies of Flipper running or another process is using the same
|
||||||
|
port(s). As a result devices will not be able to connect to Flipper.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Please try to kill the offending process by running{' '}
|
||||||
|
<code>kill $(lsof -ti:PORTNUMBER)</code> and restart flipper.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{'' + err}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>Failed to start connection server: ${err.message}</>
|
||||||
|
),
|
||||||
|
duration: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('device-connected', (device) => {
|
||||||
|
device.loadDevicePlugins(
|
||||||
|
store.getState().plugins.devicePlugins,
|
||||||
|
store.getState().connections.enabledDevicePlugins,
|
||||||
|
);
|
||||||
|
|
||||||
|
store.dispatch({
|
||||||
|
type: 'REGISTER_DEVICE',
|
||||||
|
payload: device,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('client-connected', (payload) =>
|
||||||
|
handleClientConnected(store, payload),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
server.close();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function handleClientConnected(store: Store, client: Client) {
|
||||||
|
const {connections} = store.getState();
|
||||||
|
const existingClient = connections.clients.find((c) => c.id === client.id);
|
||||||
|
|
||||||
|
if (existingClient) {
|
||||||
|
existingClient.destroy();
|
||||||
|
store.dispatch({
|
||||||
|
type: 'CLEAR_CLIENT_PLUGINS_STATE',
|
||||||
|
payload: {
|
||||||
|
clientId: client.id,
|
||||||
|
devicePlugins: new Set(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
store.dispatch({
|
||||||
|
type: 'CLIENT_REMOVED',
|
||||||
|
payload: client.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(
|
||||||
|
`Device client initialized: ${client.id}. Supported plugins: ${Array.from(
|
||||||
|
client.plugins,
|
||||||
|
).join(', ')}`,
|
||||||
|
'server',
|
||||||
|
);
|
||||||
|
|
||||||
|
store.dispatch({
|
||||||
|
type: 'NEW_CLIENT',
|
||||||
|
payload: client,
|
||||||
|
});
|
||||||
|
|
||||||
|
const device = client.deviceSync;
|
||||||
|
if (device) {
|
||||||
|
store.dispatch(selectDevice(device));
|
||||||
|
store.dispatch(selectClient(client.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
client.emit('plugins-change');
|
||||||
|
}
|
||||||
@@ -8,13 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {remote} from 'electron';
|
import {remote} from 'electron';
|
||||||
import androidDevice from '../server/androidDevice';
|
import flipperServer from './flipperServer';
|
||||||
import metroDevice from '../server/metroDevice';
|
|
||||||
import iOSDevice from '../server/iOSDevice';
|
|
||||||
import desktopDevice from './desktopDevice';
|
|
||||||
import application from './application';
|
import application from './application';
|
||||||
import tracking from './tracking';
|
import tracking from './tracking';
|
||||||
import server from '../server/server';
|
|
||||||
import notifications from './notifications';
|
import notifications from './notifications';
|
||||||
import plugins from './plugins';
|
import plugins from './plugins';
|
||||||
import user from './fb-stubs/user';
|
import user from './fb-stubs/user';
|
||||||
@@ -38,12 +34,8 @@ export default function (store: Store, logger: Logger): () => Promise<void> {
|
|||||||
}
|
}
|
||||||
const dispatchers: Array<Dispatcher> = [
|
const dispatchers: Array<Dispatcher> = [
|
||||||
application,
|
application,
|
||||||
store.getState().settingsState.enableAndroid ? androidDevice : null,
|
|
||||||
iOSDevice,
|
|
||||||
metroDevice,
|
|
||||||
desktopDevice,
|
|
||||||
tracking,
|
tracking,
|
||||||
server,
|
flipperServer,
|
||||||
notifications,
|
notifications,
|
||||||
plugins,
|
plugins,
|
||||||
user,
|
user,
|
||||||
|
|||||||
194
desktop/app/src/server/FlipperServer.tsx
Normal file
194
desktop/app/src/server/FlipperServer.tsx
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/**
|
||||||
|
* 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 EventEmitter from 'events';
|
||||||
|
import Client from '../Client';
|
||||||
|
import {Store} from '../reducers/index';
|
||||||
|
import {Logger} from '../fb-interfaces/Logger';
|
||||||
|
import ServerController from './comms/ServerController';
|
||||||
|
import {UninitializedClient} from '../UninitializedClient';
|
||||||
|
import {addErrorNotification} from '../reducers/notifications';
|
||||||
|
import {CertificateExchangeMedium} from '../server/utils/CertificateProvider';
|
||||||
|
import {isLoggedIn} from '../fb-stubs/user';
|
||||||
|
import React from 'react';
|
||||||
|
import {Typography} from 'antd';
|
||||||
|
import {ACTIVE_SHEET_SIGN_IN, setActiveSheet} from '../reducers/application';
|
||||||
|
import androidDevice from './androidDevice';
|
||||||
|
import iOSDevice from './iOSDevice';
|
||||||
|
import metroDevice from './metroDevice';
|
||||||
|
import desktopDevice from './desktopDevice';
|
||||||
|
import BaseDevice from './devices/BaseDevice';
|
||||||
|
|
||||||
|
type FlipperServerEvents = {
|
||||||
|
'device-connected': BaseDevice;
|
||||||
|
'client-connected': Client;
|
||||||
|
'server-start-error': any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface FlipperServerConfig {
|
||||||
|
enableAndroid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startFlipperServer(
|
||||||
|
config: FlipperServerConfig,
|
||||||
|
store: Store,
|
||||||
|
logger: Logger,
|
||||||
|
): Promise<FlipperServer> {
|
||||||
|
const server = new FlipperServer(config, store, logger);
|
||||||
|
|
||||||
|
await server.start();
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlipperServer takes care of all incoming device & client connections.
|
||||||
|
* It will set up managers per device type, and create the incoming
|
||||||
|
* RSocket/WebSocket server to handle incoming client connections.
|
||||||
|
*
|
||||||
|
* The server should be largely treated as event emitter, by listening to the relevant events
|
||||||
|
* using '.on'. All events are strongly typed.
|
||||||
|
*/
|
||||||
|
export class FlipperServer {
|
||||||
|
private readonly events = new EventEmitter();
|
||||||
|
readonly server: ServerController;
|
||||||
|
readonly disposers: ((() => void) | void)[] = [];
|
||||||
|
|
||||||
|
// TODO: remove store argument
|
||||||
|
constructor(
|
||||||
|
public config: FlipperServerConfig,
|
||||||
|
/** @deprecated remove! */
|
||||||
|
public store: Store,
|
||||||
|
public logger: Logger,
|
||||||
|
) {
|
||||||
|
this.server = new ServerController(logger, store);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
async start() {
|
||||||
|
const server = this.server;
|
||||||
|
|
||||||
|
server.addListener('new-client', (client: Client) => {
|
||||||
|
this.emit('client-connected', client);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addListener('error', (err) => {
|
||||||
|
this.emit('server-start-error', err);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addListener('start-client-setup', (client: UninitializedClient) => {
|
||||||
|
this.store.dispatch({
|
||||||
|
type: 'START_CLIENT_SETUP',
|
||||||
|
payload: client,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addListener(
|
||||||
|
'finish-client-setup',
|
||||||
|
(payload: {client: UninitializedClient; deviceId: string}) => {
|
||||||
|
this.store.dispatch({
|
||||||
|
type: 'FINISH_CLIENT_SETUP',
|
||||||
|
payload: payload,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.addListener(
|
||||||
|
'client-setup-error',
|
||||||
|
({client, error}: {client: UninitializedClient; error: Error}) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
addErrorNotification(
|
||||||
|
`Connection to '${client.appName}' on '${client.deviceName}' failed`,
|
||||||
|
'Failed to start client connection',
|
||||||
|
error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
server.addListener(
|
||||||
|
'client-unresponsive-error',
|
||||||
|
({
|
||||||
|
client,
|
||||||
|
medium,
|
||||||
|
}: {
|
||||||
|
client: UninitializedClient;
|
||||||
|
medium: CertificateExchangeMedium;
|
||||||
|
deviceID: string;
|
||||||
|
}) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
addErrorNotification(
|
||||||
|
`Timed out establishing connection with "${client.appName}" on "${client.deviceName}".`,
|
||||||
|
medium === 'WWW' ? (
|
||||||
|
<>
|
||||||
|
Verify that both your computer and mobile device are on
|
||||||
|
Lighthouse/VPN{' '}
|
||||||
|
{!isLoggedIn().get() && (
|
||||||
|
<>
|
||||||
|
and{' '}
|
||||||
|
<Typography.Link
|
||||||
|
onClick={() =>
|
||||||
|
this.store.dispatch(
|
||||||
|
setActiveSheet(ACTIVE_SHEET_SIGN_IN),
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
log in to Facebook Intern
|
||||||
|
</Typography.Link>
|
||||||
|
</>
|
||||||
|
)}{' '}
|
||||||
|
so they can exchange certificates.{' '}
|
||||||
|
<Typography.Link href="https://www.internalfb.com/intern/wiki/Ops/Network/Enterprise_Network_Engineering/ene_wlra/VPN_Help/Vpn/mobile/">
|
||||||
|
Check this link
|
||||||
|
</Typography.Link>{' '}
|
||||||
|
on how to enable VPN on mobile device.
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Verify that your client is connected to Flipper and that there is no error related to idb.'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await server.init();
|
||||||
|
await this.startDeviceListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
async startDeviceListeners() {
|
||||||
|
if (this.config.enableAndroid) {
|
||||||
|
this.disposers.push(await androidDevice(this.store, this.logger));
|
||||||
|
}
|
||||||
|
this.disposers.push(
|
||||||
|
iOSDevice(this.store, this.logger),
|
||||||
|
metroDevice(this.store, this.logger),
|
||||||
|
desktopDevice(this),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
on<Event extends keyof FlipperServerEvents>(
|
||||||
|
event: Event,
|
||||||
|
callback: (payload: FlipperServerEvents[Event]) => void,
|
||||||
|
): void {
|
||||||
|
this.events.on(event, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
emit<Event extends keyof FlipperServerEvents>(
|
||||||
|
event: Event,
|
||||||
|
payload: FlipperServerEvents[Event],
|
||||||
|
): void {
|
||||||
|
this.events.emit(event, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async close() {
|
||||||
|
this.server.close();
|
||||||
|
this.disposers.forEach((f) => f?.());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,11 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Store} from '../reducers/index';
|
|
||||||
import {Logger} from '../fb-interfaces/Logger';
|
|
||||||
|
|
||||||
import MacDevice from '../server/devices/MacDevice';
|
import MacDevice from '../server/devices/MacDevice';
|
||||||
import WindowsDevice from '../server/devices/WindowsDevice';
|
import WindowsDevice from '../server/devices/WindowsDevice';
|
||||||
|
import {FlipperServer} from './FlipperServer';
|
||||||
|
|
||||||
export default (store: Store, _logger: Logger) => {
|
export default (flipperServer: FlipperServer) => {
|
||||||
let device;
|
let device;
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
device = new MacDevice();
|
device = new MacDevice();
|
||||||
@@ -22,12 +20,5 @@ export default (store: Store, _logger: Logger) => {
|
|||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
device.loadDevicePlugins(
|
flipperServer.emit('device-connected', device);
|
||||||
store.getState().plugins.devicePlugins,
|
|
||||||
store.getState().connections.enabledDevicePlugins,
|
|
||||||
);
|
|
||||||
store.dispatch({
|
|
||||||
type: 'REGISTER_DEVICE',
|
|
||||||
payload: device,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 ServerController from '../server/comms/ServerController';
|
|
||||||
|
|
||||||
import {Store} from '../reducers/index';
|
|
||||||
import {Logger} from '../fb-interfaces/Logger';
|
|
||||||
import Client from '../Client';
|
|
||||||
import {UninitializedClient} from '../UninitializedClient';
|
|
||||||
import {addErrorNotification} from '../reducers/notifications';
|
|
||||||
import {CertificateExchangeMedium} from '../server/utils/CertificateProvider';
|
|
||||||
import {selectClient, selectDevice} from '../reducers/connections';
|
|
||||||
import {isLoggedIn} from '../fb-stubs/user';
|
|
||||||
import React from 'react';
|
|
||||||
import {notification, Typography} from 'antd';
|
|
||||||
import {ACTIVE_SHEET_SIGN_IN, setActiveSheet} from '../reducers/application';
|
|
||||||
|
|
||||||
export default (store: Store, logger: Logger) => {
|
|
||||||
const server = new ServerController(logger, store);
|
|
||||||
server.init();
|
|
||||||
|
|
||||||
server.addListener('new-client', (client: Client) => {
|
|
||||||
registerNewClient(store, client);
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addListener('error', (err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Failed to start connection server',
|
|
||||||
description:
|
|
||||||
err.code === 'EADDRINUSE' ? (
|
|
||||||
<>
|
|
||||||
Couldn't start connection server. Looks like you have multiple
|
|
||||||
copies of Flipper running or another process is using the same
|
|
||||||
port(s). As a result devices will not be able to connect to Flipper.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Please try to kill the offending process by running{' '}
|
|
||||||
<code>kill $(lsof -ti:PORTNUMBER)</code> and restart flipper.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{'' + err}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>Failed to start connection server: ${err.message}</>
|
|
||||||
),
|
|
||||||
duration: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addListener('start-client-setup', (client: UninitializedClient) => {
|
|
||||||
store.dispatch({
|
|
||||||
type: 'START_CLIENT_SETUP',
|
|
||||||
payload: client,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.addListener(
|
|
||||||
'finish-client-setup',
|
|
||||||
(payload: {client: UninitializedClient; deviceId: string}) => {
|
|
||||||
store.dispatch({
|
|
||||||
type: 'FINISH_CLIENT_SETUP',
|
|
||||||
payload: payload,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
server.addListener(
|
|
||||||
'client-setup-error',
|
|
||||||
({client, error}: {client: UninitializedClient; error: Error}) => {
|
|
||||||
store.dispatch(
|
|
||||||
addErrorNotification(
|
|
||||||
`Connection to '${client.appName}' on '${client.deviceName}' failed`,
|
|
||||||
'Failed to start client connection',
|
|
||||||
error,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
server.addListener(
|
|
||||||
'client-unresponsive-error',
|
|
||||||
({
|
|
||||||
client,
|
|
||||||
medium,
|
|
||||||
}: {
|
|
||||||
client: UninitializedClient;
|
|
||||||
medium: CertificateExchangeMedium;
|
|
||||||
deviceID: string;
|
|
||||||
}) => {
|
|
||||||
store.dispatch(
|
|
||||||
addErrorNotification(
|
|
||||||
`Timed out establishing connection with "${client.appName}" on "${client.deviceName}".`,
|
|
||||||
medium === 'WWW' ? (
|
|
||||||
<>
|
|
||||||
Verify that both your computer and mobile device are on
|
|
||||||
Lighthouse/VPN{' '}
|
|
||||||
{!isLoggedIn().get() && (
|
|
||||||
<>
|
|
||||||
and{' '}
|
|
||||||
<Typography.Link
|
|
||||||
onClick={() =>
|
|
||||||
store.dispatch(setActiveSheet(ACTIVE_SHEET_SIGN_IN))
|
|
||||||
}>
|
|
||||||
log in to Facebook Intern
|
|
||||||
</Typography.Link>
|
|
||||||
</>
|
|
||||||
)}{' '}
|
|
||||||
so they can exchange certificates.{' '}
|
|
||||||
<Typography.Link href="https://www.internalfb.com/intern/wiki/Ops/Network/Enterprise_Network_Engineering/ene_wlra/VPN_Help/Vpn/mobile/">
|
|
||||||
Check this link
|
|
||||||
</Typography.Link>{' '}
|
|
||||||
on how to enable VPN on mobile device.
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
'Verify that your client is connected to Flipper and that there is no error related to idb.'
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
window.addEventListener('beforeunload', () => {
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return server.close;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function registerNewClient(store: Store, client: Client) {
|
|
||||||
const {connections} = store.getState();
|
|
||||||
const existingClient = connections.clients.find((c) => c.id === client.id);
|
|
||||||
|
|
||||||
if (existingClient) {
|
|
||||||
existingClient.destroy();
|
|
||||||
store.dispatch({
|
|
||||||
type: 'CLEAR_CLIENT_PLUGINS_STATE',
|
|
||||||
payload: {
|
|
||||||
clientId: client.id,
|
|
||||||
devicePlugins: new Set(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
store.dispatch({
|
|
||||||
type: 'CLIENT_REMOVED',
|
|
||||||
payload: client.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch({
|
|
||||||
type: 'NEW_CLIENT',
|
|
||||||
payload: client,
|
|
||||||
});
|
|
||||||
|
|
||||||
const device = client.deviceSync;
|
|
||||||
if (device) {
|
|
||||||
store.dispatch(selectDevice(device));
|
|
||||||
store.dispatch(selectClient(client.id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user