diff --git a/packages/dev-middleware/src/createDevMiddleware.js b/packages/dev-middleware/src/createDevMiddleware.js index f11f8c27fa0ed7..237238111e9c2a 100644 --- a/packages/dev-middleware/src/createDevMiddleware.js +++ b/packages/dev-middleware/src/createDevMiddleware.js @@ -9,6 +9,7 @@ */ import type {CreateCustomMessageHandlerFn} from './inspector-proxy/CustomMessageHandler'; +import type {HasConnectedDevicesListener} from './inspector-proxy/InspectorProxy'; import type {BrowserLauncher} from './types/BrowserLauncher'; import type {EventReporter, ReportableEvent} from './types/EventReporter'; import type {Experiments, ExperimentsConfig} from './types/Experiments'; @@ -73,6 +74,8 @@ type Options = $ReadOnly<{ type DevMiddlewareAPI = $ReadOnly<{ middleware: NextHandleFunction, websocketEndpoints: {[path: string]: ws$WebSocketServer}, + unstable_hasConnectedDevices(): boolean, + unstable_addHasConnectedDevicesListener: HasConnectedDevicesListener, }>; export default function createDevMiddleware({ @@ -131,6 +134,10 @@ export default function createDevMiddleware({ return { middleware, websocketEndpoints: inspectorProxy.createWebSocketListeners(), + unstable_hasConnectedDevices: () => + inspectorProxy.unstable_hasConnectedDevices(), + unstable_addHasConnectedDevicesListener: cb => + inspectorProxy.unstable_addHasConnectedDevicesListener(cb), }; } diff --git a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js index f9f419b1124d75..77343a19b458d9 100644 --- a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js +++ b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js @@ -71,6 +71,12 @@ export interface InspectorProxyQueries { ): Array; } +export type RemoveHasConnectedDevicesListener = () => void; + +export type HasConnectedDevicesListener = ( + callback: (hasConnectedDevices: boolean) => void, +) => RemoveHasConnectedDevicesListener; + /** * Main Inspector Proxy class that connects JavaScript VM inside Android/iOS apps and JS debugger. */ @@ -97,6 +103,8 @@ export default class InspectorProxy implements InspectorProxyQueries { #eventLoopPerfTracker: EventLoopPerfTracker; + #onHasConnectedDevicesChangedFns: Set<(boolean) => void>; + constructor( serverBaseUrl: string, eventReporter: ?EventReporter, @@ -111,6 +119,7 @@ export default class InspectorProxy implements InspectorProxyQueries { this.#experiments = experiments; this.#logger = logger; this.#customMessageHandler = customMessageHandler; + this.#onHasConnectedDevicesChangedFns = new Set(); if (trackEventLoopPerf) { this.#eventLoopPerfTracker = new EventLoopPerfTracker({ perfMeasurementDuration: EVENT_LOOP_PERF_MEASUREMENT_MS, @@ -142,6 +151,18 @@ export default class InspectorProxy implements InspectorProxyQueries { } } + unstable_hasConnectedDevices(): boolean { + return this.#devices.size > 0; + } + + unstable_addHasConnectedDevicesListener: HasConnectedDevicesListener = + onDevicesChanged => { + this.#onHasConnectedDevicesChangedFns.add(onDevicesChanged); + return () => { + this.#onHasConnectedDevicesChangedFns.delete(onDevicesChanged); + }; + }; + getPageDescriptions({ requestorRelativeBaseUrl, logNoPagesForConnectedDevice = false, @@ -365,6 +386,9 @@ export default class InspectorProxy implements InspectorProxyQueries { } this.#devices.set(deviceId, newDevice); + if (this.#devices.size === 1) { + this.#onHasConnectedDevicesChangedFns.forEach(cb => cb(true)); + } debug( "Got new device connection: name='%s', app=%s, device=%s, via=%s", @@ -457,6 +481,9 @@ export default class InspectorProxy implements InspectorProxyQueries { if (this.#devices.get(deviceId)?.dangerouslyGetSocket() === socket) { this.#devices.delete(deviceId); + if (this.#devices.size === 0) { + this.#onHasConnectedDevicesChangedFns.forEach(cb => cb(false)); + } } }); } catch (error) {