Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix[ReactDevToolsViewBase]: observe model from constructor #112

Merged
merged 4 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions front_end/entrypoints/rn_fusebox/rn_fusebox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ class FuseboxReactNativeApplicationObserver implements
const {appDisplayName, deviceName} = event.data;

// Update window title
if (appDisplayName != null) {
document.title = `${appDisplayName}${deviceName != null ? ` (${deviceName})` : ''} - React Native DevTools`;
if (appDisplayName !== null && appDisplayName !== undefined) {
document.title = `${appDisplayName}${deviceName !== null && deviceName !== undefined ? ` (${deviceName})` : ''} - React Native DevTools`;
}
}
}
Expand Down
32 changes: 18 additions & 14 deletions front_end/panels/react_devtools/ReactDevToolsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
readonly #listeners: Set<ReactDevToolsTypes.WallListener> = new Set();
#initializeCalled: boolean = false;
#initialized: boolean = false;
#bridge: ReactDevToolsTypes.Bridge | null;
#store: ReactDevToolsTypes.Store | null;
#bridge: ReactDevToolsTypes.Bridge | null = null;
#store: ReactDevToolsTypes.Store | null = null;

constructor(target: SDK.Target.Target) {
super(target);
Expand All @@ -52,11 +52,9 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
},
send: (event, payload): void => void this.#sendMessage({event, payload}),
};
this.#bridge = ReactDevTools.createBridge(this.#wall);
this.#store = ReactDevTools.createStore(this.#bridge);

const bindingsModel = target.model(ReactNativeModels.ReactDevToolsBindingsModel.ReactDevToolsBindingsModel);
if (bindingsModel == null) {
if (bindingsModel === null) {
throw new Error('Failed to construct ReactDevToolsModel: ReactDevToolsBindingsModel was null');
}

Expand All @@ -82,13 +80,16 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
window.addEventListener('beforeunload', () => this.#bridge?.shutdown());
}

async ensureInitialized(): Promise<void> {
ensureInitialized(): void {
if (this.#initializeCalled) {
return;
}

this.#initializeCalled = true;
void this.#initialize();
}

async #initialize(): Promise<void> {
try {
const bindingsModel = this.#bindingsModel;
await bindingsModel.enable();
Expand All @@ -101,7 +102,7 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
await bindingsModel.initializeDomain(ReactDevToolsModel.FUSEBOX_BINDING_NAMESPACE);

this.#initialized = true;
this.dispatchEventToListeners(Events.InitializationCompleted);
this.#finishInitializationAndNotify();
} catch (e) {
this.dispatchEventToListeners(Events.InitializationFailed, e.message);
}
Expand All @@ -112,15 +113,15 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
}

getBridgeOrThrow(): ReactDevToolsTypes.Bridge {
if (this.#bridge == null) {
if (this.#bridge === null) {
throw new Error('Failed to get bridge from ReactDevToolsModel: bridge was null');
}

return this.#bridge;
}

getStoreOrThrow(): ReactDevToolsTypes.Store {
if (this.#store == null) {
if (this.#store === null) {
throw new Error('Failed to get store from ReactDevToolsModel: store was null');
}

Expand Down Expand Up @@ -152,17 +153,20 @@ export class ReactDevToolsModel extends SDK.SDKModel.SDKModel<EventTypes> {
throw new Error('ReactDevToolsModel failed to handle BackendExecutionContextCreated event: ReactDevToolsBindingsModel was null');
}

this.#bridge = ReactDevTools.createBridge(this.#wall);
this.#store = ReactDevTools.createStore(this.#bridge);

// This could happen if the app was reloaded while ReactDevToolsBindingsModel was initializing
if (!rdtBindingsModel.isEnabled()) {
void this.ensureInitialized();
this.ensureInitialized();
} else {
this.dispatchEventToListeners(Events.InitializationCompleted);
this.#finishInitializationAndNotify();
}
}

#finishInitializationAndNotify(): void {
this.#bridge = ReactDevTools.createBridge(this.#wall);
this.#store = ReactDevTools.createStore(this.#bridge);
this.dispatchEventToListeners(Events.InitializationCompleted);
}

#handleBackendExecutionContextUnavailable({data: errorMessage}: ReactDevToolsBindingsBackendExecutionContextUnavailableEvent): void {
this.dispatchEventToListeners(Events.InitializationFailed, errorMessage);
}
Expand Down
31 changes: 17 additions & 14 deletions front_end/panels/react_devtools/ReactDevToolsViewBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {Events as ReactDevToolsModelEvents, ReactDevToolsModel, type EventTypes

import type * as ReactDevToolsTypes from '../../third_party/react-devtools/react-devtools.js';
import type * as Platform from '../../core/platform/platform.js';
import { LocalizedString } from '../../core/platform/UIString.js';

const UIStrings = {
/**
Expand Down Expand Up @@ -76,32 +75,24 @@ export class ReactDevToolsViewBase extends UI.View.SimpleView implements

constructor(
tab: 'components' | 'profiler',
title: LocalizedString,
title: Platform.UIString.LocalizedString,
) {
super(title);

this.#tab = tab;
this.#renderLoader();

SDK.TargetManager.TargetManager.instance().observeModels(ReactDevToolsModel, this);
}

override wasShown(): void {
super.wasShown();
this.registerCSSFiles([ReactDevTools.CSS]);

if (this.#model == null) {
SDK.TargetManager.TargetManager.instance().observeModels(ReactDevToolsModel, this);
}
}

modelAdded(model: ReactDevToolsModel): void {
this.#model = model;

if (model.isInitialized()) {
// Already initialized from another rendered React DevTools view - render
// from initialized state
this.#renderDevToolsView();
}

model.addEventListener(
ReactDevToolsModelEvents.InitializationCompleted,
this.#handleInitializationCompleted,
Expand All @@ -117,7 +108,15 @@ export class ReactDevToolsViewBase extends UI.View.SimpleView implements
this.#handleBackendDestroyed,
this,
);
void model.ensureInitialized();

if (model.isInitialized()) {
// Already initialized from another rendered React DevTools panel - render
// from initialized state
this.#renderDevToolsView();
} else {
// Once initialized, it will emit InitializationCompleted event
model.ensureInitialized();
}
}

modelRemoved(model: ReactDevToolsModel): void {
Expand Down Expand Up @@ -153,7 +152,11 @@ export class ReactDevToolsViewBase extends UI.View.SimpleView implements
#renderDevToolsView(): void {
this.#clearView();

const model = this.#model!;
const model = this.#model;
if (model === null) {
throw new Error('Attempted to render React DevTools panel, but the model was null');
}

const usingDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
const initializeFn = this.#tab === 'components' ? ReactDevTools.initializeComponents : ReactDevTools.initializeProfiler;

Expand Down
20 changes: 8 additions & 12 deletions front_end/panels/rn_welcome/RNWelcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ const UIStrings = {
docsReactDevTools: 'React DevTools',
/** @description "React DevTools" item detail */
docsReactDevToolsDetail: 'Debug React components with React DevTools',
/** @description "React Native DevTools" title (docs item 2 - post launch) */
docsRNDevTools: 'React Native DevTools',
/** @description "React Native DevTools" item detail */
docsRNDevToolsDetail: 'Explore features available in React Native DevTools',
/** @description "Native Debugging" title (docs item 3) */
docsNativeDebugging: 'Native Debugging',
/** @description "Native Debugging" item detail */
Expand All @@ -53,7 +49,7 @@ type RNWelcomeOptions = {
debuggerBrandName: () => Platform.UIString.LocalizedString,
showBetaLabel?: boolean,
showTechPreviewLabel?: boolean,
showDocs?: boolean
showDocs?: boolean,
};

export class RNWelcomeImpl extends UI.Widget.VBox implements
Expand Down Expand Up @@ -106,7 +102,7 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements
}
}

private _handleLinkPress(url: string): void {
#handleLinkPress(url: string): void {
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(
url as Platform.DevToolsPath.UrlString,
);
Expand All @@ -117,7 +113,7 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements
debuggerBrandName,
showBetaLabel = false,
showTechPreviewLabel = false,
showDocs = false
showDocs = false,
} = this.options;
const welcomeIconUrl = new URL(
'../../Images/react_native/welcomeIcon.png',
Expand All @@ -140,7 +136,7 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements
<div class="rn-welcome-panel">
<header class="rn-welcome-hero">
<div class="rn-welcome-heading">
<img class="rn-welcome-icon" src="${welcomeIconUrl}" role="presentation" />
<img class="rn-welcome-icon" src=${welcomeIconUrl} role="presentation" />
<h1 class="rn-welcome-title">
${debuggerBrandName()}
</h1>
Expand All @@ -166,29 +162,29 @@ export class RNWelcomeImpl extends UI.Widget.VBox implements
${i18nString(UIStrings.whatsNewLabel)}
</x-link>
</div>
${this.#reactNativeVersion != null ? html`
${this.#reactNativeVersion !== null && this.#reactNativeVersion !== undefined ? html`
<p class="rn-welcome-version">React Native: <code>${this.#reactNativeVersion}</code></p>
` : null}
</header>
${showDocs ? html`
<section class="rn-welcome-docsfeed">
<h2 class="rn-welcome-h2">Learn</h2>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this._handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging')} title="${i18nString(UIStrings.docsDebuggingBasics)}">
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging')} title=${i18nString(UIStrings.docsDebuggingBasics)}>
<div class="rn-welcome-image" style="background-image: url('${docsImage1Url}')"></div>
<div>
<p class="devtools-link">${i18nString(UIStrings.docsDebuggingBasics)}</p>
<p>${i18nString(UIStrings.docsDebuggingBasicsDetail)}</p>
</div>
</button>
<!-- TODO(huntie): Replace this item when React Native DevTools docs are complete -->
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this._handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging/react-devtools')} title="${i18nString(UIStrings.docsReactDevTools)}">
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging/react-devtools')} title=${i18nString(UIStrings.docsReactDevTools)}>
<div class="rn-welcome-image" style="background-image: url('${docsImage2Url}')"></div>
<div>
<p class="devtools-link">${i18nString(UIStrings.docsReactDevTools)}</p>
<p>${i18nString(UIStrings.docsReactDevToolsDetail)}</p>
</div>
</button>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this._handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging/debugging-native-code')} title="${i18nString(UIStrings.docsNativeDebugging)}">
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#handleLinkPress.bind(this, 'https:\/\/reactnative.dev/docs/debugging/debugging-native-code')} title=${i18nString(UIStrings.docsNativeDebugging)}>
<div class="rn-welcome-image" style="background-image: url('${docsImage3Url}')"></div>
<div>
<p class="devtools-link">${i18nString(UIStrings.docsNativeDebugging)}</p>
Expand Down
1 change: 1 addition & 0 deletions scripts/eslint_rules/lib/check_license_header.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const EXCLUDED_FILES = [
const META_CODE_PATHS = [
'core/host/RNPerfMetrics.ts',
'core/rn_experiments',
'core/sdk/ReactNativeApplicationModel.ts',
'entrypoints/rn_fusebox',
'entrypoints/rn_inspector',
'entrypoints/shell/browser_compatibility_guard.ts',
Expand Down
Loading