migrate redux store
Summary: Migrating redux stores to TypeScript Reviewed By: passy Differential Revision: D16579796 fbshipit-source-id: e3e507f17f1bdd57eb45e30cb0b28aaee6c4521c
This commit is contained in:
committed by
Facebook Github Bot
parent
2c95ef6b25
commit
64cefd0f84
388
src/reducers/connections.tsx
Normal file
388
src/reducers/connections.tsx
Normal file
@@ -0,0 +1,388 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import MacDevice from '../devices/MacDevice';
|
||||
import Client from '../Client';
|
||||
import {UninitializedClient} from '../UninitializedClient';
|
||||
import {isEqual} from 'lodash';
|
||||
import iosUtil from '../fb-stubs/iOSContainerUtility';
|
||||
import {performance} from 'perf_hooks';
|
||||
|
||||
export type State = {
|
||||
devices: Array<BaseDevice>,
|
||||
androidEmulators: Array<string>,
|
||||
selectedDevice: null | BaseDevice,
|
||||
selectedPlugin: null | string,
|
||||
selectedApp: null | string,
|
||||
userPreferredDevice: null | string,
|
||||
userPreferredPlugin: null | string,
|
||||
userPreferredApp: null | string,
|
||||
error: null | string,
|
||||
clients: Array<Client>,
|
||||
uninitializedClients: Array<{
|
||||
client: UninitializedClient,
|
||||
deviceId?: string,
|
||||
errorMessage?: string,
|
||||
}>,
|
||||
deepLinkPayload: null | string,
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'UNREGISTER_DEVICES',
|
||||
payload: Set<string>,
|
||||
}
|
||||
| {
|
||||
type: 'REGISTER_DEVICE',
|
||||
payload: BaseDevice,
|
||||
}
|
||||
| {
|
||||
type: 'REGISTER_ANDROID_EMULATORS',
|
||||
payload: Array<string>,
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_DEVICE',
|
||||
payload: BaseDevice,
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_PLUGIN',
|
||||
payload: {
|
||||
selectedPlugin: null | string,
|
||||
selectedApp?: null | string,
|
||||
deepLinkPayload: null | string,
|
||||
},
|
||||
}
|
||||
| {
|
||||
type: 'SELECT_USER_PREFERRED_PLUGIN',
|
||||
payload: string,
|
||||
}
|
||||
| {
|
||||
type: 'SERVER_ERROR',
|
||||
payload: null | string,
|
||||
}
|
||||
| {
|
||||
type: 'NEW_CLIENT',
|
||||
payload: Client,
|
||||
}
|
||||
| {
|
||||
type: 'NEW_CLIENT_SANITY_CHECK',
|
||||
payload: Client,
|
||||
}
|
||||
| {
|
||||
type: 'CLIENT_REMOVED',
|
||||
payload: string,
|
||||
}
|
||||
| {
|
||||
type: 'PREFER_DEVICE',
|
||||
payload: string,
|
||||
}
|
||||
| {
|
||||
type: 'START_CLIENT_SETUP',
|
||||
payload: UninitializedClient,
|
||||
}
|
||||
| {
|
||||
type: 'FINISH_CLIENT_SETUP',
|
||||
payload: {client: UninitializedClient, deviceId: string},
|
||||
}
|
||||
| {
|
||||
type: 'CLIENT_SETUP_ERROR',
|
||||
payload: {client: UninitializedClient, error: Error},
|
||||
};
|
||||
|
||||
const DEFAULT_PLUGIN = 'DeviceLogs';
|
||||
const DEFAULT_DEVICE_BLACKLIST = [MacDevice];
|
||||
|
||||
const INITAL_STATE: State = {
|
||||
devices: [],
|
||||
androidEmulators: [],
|
||||
selectedDevice: null,
|
||||
selectedApp: null,
|
||||
selectedPlugin: DEFAULT_PLUGIN,
|
||||
userPreferredDevice: null,
|
||||
userPreferredPlugin: null,
|
||||
userPreferredApp: null,
|
||||
error: null,
|
||||
clients: [],
|
||||
uninitializedClients: [],
|
||||
deepLinkPayload: null,
|
||||
};
|
||||
|
||||
const reducer = (state: State = INITAL_STATE, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case 'SELECT_DEVICE': {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
selectedApp: null,
|
||||
selectedPlugin: DEFAULT_PLUGIN,
|
||||
selectedDevice: payload,
|
||||
userPreferredDevice: payload.title,
|
||||
};
|
||||
}
|
||||
case 'REGISTER_ANDROID_EMULATORS': {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
androidEmulators: payload,
|
||||
};
|
||||
}
|
||||
case 'REGISTER_DEVICE': {
|
||||
const {payload} = action;
|
||||
const devices = state.devices.concat(payload);
|
||||
let {selectedDevice, selectedPlugin} = state;
|
||||
|
||||
// select the default plugin
|
||||
let selection: Partial<State> = {
|
||||
selectedApp: null,
|
||||
selectedPlugin: DEFAULT_PLUGIN,
|
||||
};
|
||||
|
||||
const canBeDefaultDevice = !DEFAULT_DEVICE_BLACKLIST.some(
|
||||
blacklistedDevice => payload instanceof blacklistedDevice,
|
||||
);
|
||||
|
||||
if (!selectedDevice && canBeDefaultDevice) {
|
||||
selectedDevice = payload;
|
||||
if (selectedPlugin) {
|
||||
// We already had a plugin selected, but no device. This is happening
|
||||
// when the Client connected before the Device.
|
||||
selection = {};
|
||||
}
|
||||
} else if (payload.title === state.userPreferredDevice) {
|
||||
selectedDevice = payload;
|
||||
} else {
|
||||
// We didn't select the newly connected device, so we don't want to
|
||||
// change the plugin.
|
||||
selection = {};
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
devices,
|
||||
// select device if none was selected before
|
||||
selectedDevice,
|
||||
...selection,
|
||||
};
|
||||
}
|
||||
case 'UNREGISTER_DEVICES': {
|
||||
const {payload} = action;
|
||||
const {selectedDevice} = state;
|
||||
let selectedDeviceWasRemoved = false;
|
||||
const devices = state.devices.filter((device: BaseDevice) => {
|
||||
if (payload.has(device.serial)) {
|
||||
if (selectedDevice === device) {
|
||||
// removed device is the selected
|
||||
selectedDeviceWasRemoved = true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
let selection = {};
|
||||
if (selectedDeviceWasRemoved) {
|
||||
selection = {
|
||||
selectedDevice: devices[devices.length - 1] || null,
|
||||
selectedApp: null,
|
||||
selectedPlugin: DEFAULT_PLUGIN,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
devices,
|
||||
...selection,
|
||||
};
|
||||
}
|
||||
case 'SELECT_PLUGIN': {
|
||||
const {payload} = action;
|
||||
const {selectedPlugin, selectedApp} = payload;
|
||||
if (selectedPlugin) {
|
||||
performance.mark(`activePlugin-${selectedPlugin}`);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
userPreferredApp: selectedApp || state.userPreferredApp,
|
||||
userPreferredPlugin: selectedPlugin,
|
||||
};
|
||||
}
|
||||
case 'SELECT_USER_PREFERRED_PLUGIN': {
|
||||
const {payload} = action;
|
||||
return {...state, userPreferredPlugin: payload};
|
||||
}
|
||||
case 'NEW_CLIENT': {
|
||||
const {payload} = action;
|
||||
const {userPreferredApp, userPreferredPlugin} = state;
|
||||
let {selectedApp, selectedPlugin} = state;
|
||||
|
||||
if (
|
||||
userPreferredApp &&
|
||||
userPreferredPlugin &&
|
||||
payload.id === userPreferredApp &&
|
||||
payload.plugins.includes(userPreferredPlugin)
|
||||
) {
|
||||
// user preferred client did reconnect, so let's select it
|
||||
selectedApp = userPreferredApp;
|
||||
selectedPlugin = userPreferredPlugin;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
clients: state.clients.concat(payload),
|
||||
uninitializedClients: state.uninitializedClients.filter(c => {
|
||||
return (
|
||||
c.deviceId !== payload.query.device_id ||
|
||||
c.client.appName !== payload.query.app
|
||||
);
|
||||
}),
|
||||
selectedApp,
|
||||
selectedPlugin,
|
||||
};
|
||||
}
|
||||
case 'NEW_CLIENT_SANITY_CHECK': {
|
||||
const {payload} = action;
|
||||
// Check for clients initialised when there is no matching device
|
||||
const clientIsStillConnected = state.clients.filter(
|
||||
client => client.id == payload.query.device_id,
|
||||
);
|
||||
if (clientIsStillConnected) {
|
||||
const matchingDeviceForClient = state.devices.filter(
|
||||
device => payload.query.device_id === device.serial,
|
||||
);
|
||||
if (matchingDeviceForClient.length === 0) {
|
||||
console.error(
|
||||
`Client initialised for non-displayed device: ${payload.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
case 'CLIENT_REMOVED': {
|
||||
const {payload} = action;
|
||||
|
||||
const selected: Partial<State> = {};
|
||||
if (state.selectedApp === payload) {
|
||||
selected.selectedApp = null;
|
||||
selected.selectedPlugin = DEFAULT_PLUGIN;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
...selected,
|
||||
clients: state.clients.filter(
|
||||
(client: Client) => client.id !== payload,
|
||||
),
|
||||
};
|
||||
}
|
||||
case 'PREFER_DEVICE': {
|
||||
const {payload: userPreferredDevice} = action;
|
||||
return {...state, userPreferredDevice};
|
||||
}
|
||||
case 'SERVER_ERROR': {
|
||||
const {payload} = action;
|
||||
return {...state, error: payload};
|
||||
}
|
||||
case 'START_CLIENT_SETUP': {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
uninitializedClients: state.uninitializedClients
|
||||
.filter(entry => !isEqual(entry.client, payload))
|
||||
.concat([{client: payload}])
|
||||
.sort((a, b) => a.client.appName.localeCompare(b.client.appName)),
|
||||
};
|
||||
}
|
||||
case 'FINISH_CLIENT_SETUP': {
|
||||
const {payload} = action;
|
||||
return {
|
||||
...state,
|
||||
uninitializedClients: state.uninitializedClients
|
||||
.map(c =>
|
||||
isEqual(c.client, payload.client)
|
||||
? {...c, deviceId: payload.deviceId}
|
||||
: c,
|
||||
)
|
||||
.sort((a, b) => a.client.appName.localeCompare(b.client.appName)),
|
||||
};
|
||||
}
|
||||
case 'CLIENT_SETUP_ERROR': {
|
||||
const {payload} = action;
|
||||
|
||||
const errorMessage =
|
||||
payload.error instanceof Error ? payload.error.message : payload.error;
|
||||
console.error(
|
||||
`Client setup error: ${errorMessage} while setting up client: ${
|
||||
payload.client.os
|
||||
}:${payload.client.deviceName}:${payload.client.appName}`,
|
||||
);
|
||||
return {
|
||||
...state,
|
||||
uninitializedClients: state.uninitializedClients
|
||||
.map(c =>
|
||||
isEqual(c.client, payload.client)
|
||||
? {...c, errorMessage: errorMessage}
|
||||
: c,
|
||||
)
|
||||
.sort((a, b) => a.client.appName.localeCompare(b.client.appName)),
|
||||
error: `Client setup error: ${errorMessage}`,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default (state: State = INITAL_STATE, action: Action): State => {
|
||||
const nextState = reducer(state, action);
|
||||
|
||||
if (nextState.selectedDevice) {
|
||||
const {selectedDevice} = nextState;
|
||||
const deviceNotSupportedError = 'iOS Devices are not yet supported';
|
||||
const error =
|
||||
selectedDevice.os === 'iOS' &&
|
||||
selectedDevice.deviceType === 'physical' &&
|
||||
!iosUtil.isAvailable()
|
||||
? deviceNotSupportedError
|
||||
: null;
|
||||
|
||||
if (nextState.error === deviceNotSupportedError) {
|
||||
nextState.error = error;
|
||||
} else {
|
||||
nextState.error = error || nextState.error;
|
||||
}
|
||||
}
|
||||
return nextState;
|
||||
};
|
||||
|
||||
export const selectDevice = (payload: BaseDevice): Action => ({
|
||||
type: 'SELECT_DEVICE',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const preferDevice = (payload: string): Action => ({
|
||||
type: 'PREFER_DEVICE',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const selectPlugin = (payload: {
|
||||
selectedPlugin: null | string,
|
||||
selectedApp?: null | string,
|
||||
deepLinkPayload: null | string,
|
||||
}): Action => ({
|
||||
type: 'SELECT_PLUGIN',
|
||||
payload,
|
||||
});
|
||||
|
||||
export const userPreferredPlugin = (payload: string): Action => ({
|
||||
type: 'SELECT_USER_PREFERRED_PLUGIN',
|
||||
payload,
|
||||
});
|
||||
Reference in New Issue
Block a user