diff --git a/config/gni/devtools_grd_files.gni b/config/gni/devtools_grd_files.gni index d9369420d55..bfab98f651a 100644 --- a/config/gni/devtools_grd_files.gni +++ b/config/gni/devtools_grd_files.gni @@ -547,7 +547,6 @@ grd_files_release_sources = [ "front_end/panels/recorder/recorder-meta.js", "front_end/panels/recorder/recorder.js", "front_end/panels/recorder/util/util.js", - "front_end/panels/rn_welcome/ReactNativeApplicationModel.js", "front_end/panels/rn_welcome/rn_welcome-legacy-meta.js", "front_end/panels/rn_welcome/rn_welcome-meta.js", "front_end/panels/rn_welcome/rn_welcome.js", @@ -787,6 +786,7 @@ grd_files_debug_sources = [ "front_end/core/sdk/PerformanceMetricsModel.js", "front_end/core/sdk/PreloadingModel.js", "front_end/core/sdk/ProfileTreeModel.js", + "front_end/core/sdk/ReactNativeApplicationModel.js", "front_end/core/sdk/RemoteObject.js", "front_end/core/sdk/Resource.js", "front_end/core/sdk/ResourceTreeModel.js", diff --git a/front_end/core/sdk/BUILD.gn b/front_end/core/sdk/BUILD.gn index 43ea1be19cb..9d2ba99eef8 100644 --- a/front_end/core/sdk/BUILD.gn +++ b/front_end/core/sdk/BUILD.gn @@ -63,6 +63,7 @@ devtools_module("sdk") { "PreloadingModel.ts", "ProfileTreeModel.ts", "RemoteObject.ts", + "ReactNativeApplicationModel.ts", "Resource.ts", "ResourceTreeModel.ts", "RuntimeModel.ts", diff --git a/front_end/panels/rn_welcome/ReactNativeApplicationModel.ts b/front_end/core/sdk/ReactNativeApplicationModel.ts similarity index 56% rename from front_end/panels/rn_welcome/ReactNativeApplicationModel.ts rename to front_end/core/sdk/ReactNativeApplicationModel.ts index 9aa3726a7ee..38cab2997ba 100644 --- a/front_end/panels/rn_welcome/ReactNativeApplicationModel.ts +++ b/front_end/core/sdk/ReactNativeApplicationModel.ts @@ -3,33 +3,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import * as SDK from '../../core/sdk/sdk.js'; - import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js'; import type * as Protocol from '../../generated/protocol.js'; -export class ReactNativeApplicationModel extends SDK.SDKModel.SDKModel implements ProtocolProxyApi.ReactNativeApplicationDispatcher { - private enabled: boolean; - private readonly agent: ProtocolProxyApi.ReactNativeApplicationApi; +import {Capability, type Target} from './Target.js'; +import {SDKModel} from './SDKModel.js'; + +export class ReactNativeApplicationModel extends SDKModel implements ProtocolProxyApi.ReactNativeApplicationDispatcher { + #enabled: boolean; + readonly #agent: ProtocolProxyApi.ReactNativeApplicationApi; + + metadataCached: Protocol.ReactNativeApplication.MetadataUpdatedEvent | null = null; - constructor(target: SDK.Target.Target) { + constructor(target: Target) { super(target); - this.enabled = false; - this.agent = target.reactNativeApplicationAgent(); + this.#enabled = false; + this.#agent = target.reactNativeApplicationAgent(); target.registerReactNativeApplicationDispatcher(this); } ensureEnabled(): void { - if (this.enabled) { + if (this.#enabled) { return; } - void this.agent.invoke_enable(); - this.enabled = true; + void this.#agent.invoke_enable(); + this.#enabled = true; } metadataUpdated(metadata: Protocol.ReactNativeApplication.MetadataUpdatedEvent): void { + this.metadataCached = metadata; this.dispatchEventToListeners(Events.MetadataUpdated, metadata); } } @@ -42,10 +46,10 @@ export type EventTypes = { [Events.MetadataUpdated]: Protocol.ReactNativeApplication.MetadataUpdatedEvent, }; -SDK.SDKModel.SDKModel.register( +SDKModel.register( ReactNativeApplicationModel, { - capabilities: SDK.Target.Capability.None, + capabilities: Capability.None, autostart: true, }, ); diff --git a/front_end/core/sdk/sdk.ts b/front_end/core/sdk/sdk.ts index 98465db34aa..16420f9bf66 100644 --- a/front_end/core/sdk/sdk.ts +++ b/front_end/core/sdk/sdk.ts @@ -62,6 +62,7 @@ import * as PaintProfiler from './PaintProfiler.js'; import * as PerformanceMetricsModel from './PerformanceMetricsModel.js'; import * as PreloadingModel from './PreloadingModel.js'; import * as ProfileTreeModel from './ProfileTreeModel.js'; +import * as ReactNativeApplicationModel from './ReactNativeApplicationModel.js'; import * as RemoteObject from './RemoteObject.js'; import * as Resource from './Resource.js'; import * as ResourceTreeModel from './ResourceTreeModel.js'; @@ -136,6 +137,7 @@ export { PerformanceMetricsModel, PreloadingModel, ProfileTreeModel, + ReactNativeApplicationModel, RemoteObject, Resource, ResourceTreeModel, diff --git a/front_end/entrypoints/rn_fusebox/rn_fusebox.ts b/front_end/entrypoints/rn_fusebox/rn_fusebox.ts index 76561ed07ce..29c5c20d546 100644 --- a/front_end/entrypoints/rn_fusebox/rn_fusebox.ts +++ b/front_end/entrypoints/rn_fusebox/rn_fusebox.ts @@ -22,7 +22,9 @@ import * as SDK from '../../core/sdk/sdk.js'; import * as UI from '../../ui/legacy/legacy.js'; import * as Main from '../main/main.js'; +import type * as Common from '../../core/common/common.js'; import type * as Platform from '../../core/platform/platform.js'; +import type * as Protocol from '../../generated/protocol.js'; import type * as Sources from '../../panels/sources/sources.js'; import * as RNExperiments from '../../core/rn_experiments/rn_experiments.js'; @@ -222,4 +224,33 @@ UI.Toolbar.registerToolbarItem({ }, }); +class FuseboxReactNativeApplicationObserver implements + SDK.TargetManager.SDKModelObserver { + constructor(targetManager: SDK.TargetManager.TargetManager) { + targetManager.observeModels(SDK.ReactNativeApplicationModel.ReactNativeApplicationModel, this); + } + + modelAdded(model: SDK.ReactNativeApplicationModel.ReactNativeApplicationModel): void { + model.ensureEnabled(); + model.addEventListener(SDK.ReactNativeApplicationModel.Events.MetadataUpdated, this.#handleMetadataUpdated, this); + } + + modelRemoved(model: SDK.ReactNativeApplicationModel.ReactNativeApplicationModel): void { + model.removeEventListener( + SDK.ReactNativeApplicationModel.Events.MetadataUpdated, this.#handleMetadataUpdated, this); + } + + #handleMetadataUpdated( + event: Common.EventTarget.EventTargetEvent): void { + const {appDisplayName, deviceName} = event.data; + + // Update window title + if (appDisplayName != null && deviceName != null) { + document.title = `${appDisplayName} (${deviceName}) - React Native DevTools`; + } + } +} + +new FuseboxReactNativeApplicationObserver(SDK.TargetManager.TargetManager.instance()); + Host.rnPerfMetrics.entryPointLoadingFinished('rn_fusebox'); diff --git a/front_end/panels/rn_welcome/BUILD.gn b/front_end/panels/rn_welcome/BUILD.gn index d9ec622b25b..425fcf81c21 100644 --- a/front_end/panels/rn_welcome/BUILD.gn +++ b/front_end/panels/rn_welcome/BUILD.gn @@ -15,7 +15,6 @@ generate_css("css_files") { devtools_module("rn_welcome") { sources = [ "RNWelcome.ts", - "ReactNativeApplicationModel.ts", ] deps = [ diff --git a/front_end/panels/rn_welcome/RNWelcome.ts b/front_end/panels/rn_welcome/RNWelcome.ts index 269bb7316b5..318754c39fb 100644 --- a/front_end/panels/rn_welcome/RNWelcome.ts +++ b/front_end/panels/rn_welcome/RNWelcome.ts @@ -13,7 +13,6 @@ import rnWelcomeStyles from './rnWelcome.css.js'; import * as LitHtml from '../../ui/lit-html/lit-html.js'; import type * as Platform from '../../core/platform/platform.js'; import type * as Protocol from '../../generated/protocol.js'; -import {Events as ModelEvents, ReactNativeApplicationModel} from './ReactNativeApplicationModel.js'; const UIStrings = { /** @description Beta label */ @@ -53,10 +52,11 @@ type RNWelcomeOptions = { showDocs?: boolean }; -export class RNWelcomeImpl extends UI.Widget.VBox implements SDK.TargetManager.SDKModelObserver { +export class RNWelcomeImpl extends UI.Widget.VBox implements + SDK.TargetManager.SDKModelObserver { private readonly options: RNWelcomeOptions; - private reactNativeVersion: string | undefined; + #reactNativeVersion: string|undefined; static instance(options: RNWelcomeOptions): RNWelcomeImpl { if (!rnWelcomeImplInstance) { @@ -70,7 +70,8 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements SDK.TargetManager.S this.options = options; - SDK.TargetManager.TargetManager.instance().observeModels(ReactNativeApplicationModel, this); + SDK.TargetManager.TargetManager.instance().observeModels( + SDK.ReactNativeApplicationModel.ReactNativeApplicationModel, this); } override wasShown(): void { @@ -80,35 +81,31 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements SDK.TargetManager.S UI.InspectorView.InspectorView.instance().showDrawer({focus: true, hasTargetDrawer: false}); } - modelAdded(model: ReactNativeApplicationModel): void { - if (this.isShowing()) { - model.ensureEnabled(); - model.addEventListener(ModelEvents.MetadataUpdated, this._updateReactNativeVersion, this); - } - } - - modelRemoved(model: ReactNativeApplicationModel): void { - model.removeEventListener(ModelEvents.MetadataUpdated, this._updateReactNativeVersion, this); + modelAdded(model: SDK.ReactNativeApplicationModel.ReactNativeApplicationModel): void { + model.ensureEnabled(); + model.addEventListener( + SDK.ReactNativeApplicationModel.Events.MetadataUpdated, this.#handleMetadataUpdated, this); + this.#reactNativeVersion = model.metadataCached?.reactNativeVersion; } - private _handleLinkPress(url: string): void { - Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab( - url as Platform.DevToolsPath.UrlString, - ); + modelRemoved(model: SDK.ReactNativeApplicationModel.ReactNativeApplicationModel): void { + model.removeEventListener( + SDK.ReactNativeApplicationModel.Events.MetadataUpdated, this.#handleMetadataUpdated, this); } - private _updateReactNativeVersion(event: Common.EventTarget.EventTargetEvent): void { - const {appDisplayName, deviceName, reactNativeVersion} = event.data; + #handleMetadataUpdated( + event: Common.EventTarget.EventTargetEvent): void { + this.#reactNativeVersion = event.data.reactNativeVersion; - if (reactNativeVersion != null) { - this.reactNativeVersion = reactNativeVersion; + if (this.isShowing()) { this.render(); } + } - // Side-effect: Update window title - if (appDisplayName != null && deviceName != null) { - document.title = `${appDisplayName} (${deviceName}) - React Native DevTools`; - } + private _handleLinkPress(url: string): void { + Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab( + url as Platform.DevToolsPath.UrlString, + ); } render(): void { @@ -153,8 +150,8 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements SDK.TargetManager.S ${i18nString(UIStrings.whatsNewLabel)} - ${this.reactNativeVersion != null ? html` -

React Native: ${this.reactNativeVersion}

+ ${this.#reactNativeVersion != null ? html` +

React Native: ${this.#reactNativeVersion}

` : null} ${showDocs ? html`