in desktop/flipper-ui-core/src/dispatcher/handleOpenPluginDeeplink.tsx [451:557]
async function selectDevicesAndClient(
store: Store,
params: OpenPluginParams,
title: string,
isDevicePlugin: boolean,
): Promise<DeeplinkError | BaseDevice | Client> {
function findValidDevices() {
// find connected devices with the right OS.
return (
store
.getState()
.connections.devices.filter((d) => d.connected.get())
.filter(
(d) => params.devices.length === 0 || params.devices.includes(d.os),
)
// This filters out OS-level devices which are causing more confusion than good
// when used with deeplinks.
.filter(canBeDefaultDevice)
);
}
// loop until we have devices (or abort)
while (!findValidDevices().length) {
if (!(await launchDeviceDialog(store, params, title))) {
return {errorState: 'PLUGIN_DEVICE_BAIL'};
}
}
// at this point we have 1 or more valid devices
const availableDevices = findValidDevices();
console.debug(
'[deeplink] selectDevicesAndClient found at least one more valid device:',
availableDevices.map((d) => d.description),
);
// device plugin
if (isDevicePlugin) {
if (availableDevices.length === 1) {
return availableDevices[0];
}
const selectedDevice = await selectDeviceDialog(availableDevices, title);
if (!selectedDevice) {
return {errorState: 'PLUGIN_DEVICE_SELECTION_BAIL'};
}
return selectedDevice;
}
console.debug('[deeplink] Not a device plugin. Waiting for valid client.');
// wait for valid client
while (true) {
const origClients = store.getState().connections.clients;
const validClients = getAllClients(store.getState().connections)
.filter(
// correct app name, or, if not set, an app that at least supports this plugin
(c) =>
params.client
? c.query.app === params.client
: c.plugins.has(params.pluginId),
)
.filter((c) => c.connected.get())
.filter((c) => availableDevices.includes(c.device));
if (validClients.length === 1) {
return validClients[0];
}
if (validClients.length > 1) {
const selectedClient = await selectClientDialog(validClients, title);
if (!selectedClient) {
return {errorState: 'PLUGIN_CLIENT_SELECTION_BAIL'};
}
return selectedClient;
}
// no valid client yet
const result = await new Promise<boolean>((resolve) => {
const dialog = Dialog.alert({
title,
type: 'warning',
message: params.client
? `Application '${params.client}' doesn't seem to be connected yet. Please start a debug version of the app to continue.`
: `No application that supports plugin '${params.pluginId}' seems to be running. Please start a debug application that supports the plugin to continue.`,
okText: 'Cancel',
});
// eslint-disable-next-line promise/catch-or-return
dialog.then(() => resolve(false));
// eslint-disable-next-line promise/catch-or-return
waitFor(store, (state) => state.connections.clients !== origClients).then(
() => {
dialog.close();
resolve(true);
},
);
// We also want to react to changes in the available plugins and refresh.
origClients.forEach((c) =>
c.on('plugins-change', () => {
dialog.close();
resolve(true);
}),
);
});
if (!result) {
return {errorState: 'PLUGIN_CLIENT_BAIL'}; // User cancelled
}
}
}