diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm index 0c086448d40cc4..a94e7cfc4f6efa 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm @@ -801,6 +801,10 @@ + (RCTManagedPointer *)JS_NativeAsyncStorage_SpecGetAllKeysCallbackError:(id)jso return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "toggleElementInspector", @selector(toggleElementInspector), args, count); } + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_addMenuItem(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "addMenuItem", @selector(addMenuItem:), args, count); + } + static facebook::jsi::Value __hostFunction_NativeDevSettingsSpecJSI_setIsShakeToShowDevMenuEnabled(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { return static_cast(turboModule).invokeObjCMethod(rt, VoidKind, "setIsShakeToShowDevMenuEnabled", @selector(setIsShakeToShowDevMenuEnabled:), args, count); } @@ -824,6 +828,9 @@ + (RCTManagedPointer *)JS_NativeAsyncStorage_SpecGetAllKeysCallbackError:(id)jso methodMap_["toggleElementInspector"] = MethodMetadata {0, __hostFunction_NativeDevSettingsSpecJSI_toggleElementInspector}; + methodMap_["addMenuItem"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_addMenuItem}; + + methodMap_["setIsShakeToShowDevMenuEnabled"] = MethodMetadata {1, __hostFunction_NativeDevSettingsSpecJSI_setIsShakeToShowDevMenuEnabled}; diff --git a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h index 16fe9ab45456da..860b261a615ac9 100644 --- a/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h +++ b/Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h @@ -743,6 +743,7 @@ namespace facebook { - (void)setIsDebuggingRemotely:(BOOL)isDebuggingRemotelyEnabled; - (void)setProfilingEnabled:(BOOL)isProfilingEnabled; - (void)toggleElementInspector; +- (void)addMenuItem:(NSString *)title; - (void)setIsShakeToShowDevMenuEnabled:(BOOL)enabled; @end diff --git a/Libraries/NativeModules/specs/NativeDevSettings.js b/Libraries/NativeModules/specs/NativeDevSettings.js index 9741595601a74d..3a2df4cb455946 100644 --- a/Libraries/NativeModules/specs/NativeDevSettings.js +++ b/Libraries/NativeModules/specs/NativeDevSettings.js @@ -19,6 +19,7 @@ export interface Spec extends TurboModule { +setIsDebuggingRemotely: (isDebuggingRemotelyEnabled: boolean) => void; +setProfilingEnabled: (isProfilingEnabled: boolean) => void; +toggleElementInspector: () => void; + +addMenuItem: (title: string) => void; // iOS only. +setIsShakeToShowDevMenuEnabled: (enabled: boolean) => void; diff --git a/Libraries/Utilities/DevSettings.js b/Libraries/Utilities/DevSettings.js new file mode 100644 index 00000000000000..f88489c75b2dd7 --- /dev/null +++ b/Libraries/Utilities/DevSettings.js @@ -0,0 +1,52 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * @format + */ + +import NativeDevSettings from '../NativeModules/specs/NativeDevSettings'; +import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; + +class DevSettings extends NativeEventEmitter { + _menuItems: Map mixed>; + + constructor() { + super(NativeDevSettings); + + this._menuItems = new Map(); + } + + addMenuItem(title: string, handler: () => mixed) { + // Make sure items are not added multiple times. This can + // happen when hot reloading the module that registers the + // menu items. The title is used as the id which means we + // don't support multiple items with the same name. + const oldHandler = this._menuItems.get(title); + if (oldHandler != null) { + this.removeListener('didPressMenuItem', oldHandler); + } else { + NativeDevSettings.addMenuItem(title); + } + + this._menuItems.set(title, handler); + this.addListener('didPressMenuItem', event => { + if (event.title === title) { + handler(); + } + }); + } + + reload() { + NativeDevSettings.reload(); + } + + // TODO: Add other dev setting methods exposed by the native module. +} + +// Avoid including the full `NativeDevSettings` class in prod. +class NoopDevSettings { + addMenuItem(title: string, handler: () => mixed) {} + reload() {} +} + +module.exports = __DEV__ ? new DevSettings() : new NoopDevSettings(); diff --git a/RNTester/js/examples/DevSettings/DevSettingsExample.js b/RNTester/js/examples/DevSettings/DevSettingsExample.js new file mode 100644 index 00000000000000..4428c8fa0e52eb --- /dev/null +++ b/RNTester/js/examples/DevSettings/DevSettingsExample.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import * as React from 'react'; +import {Alert, Button, DevSettings} from 'react-native'; + +exports.title = 'DevSettings'; +exports.description = 'Customize the development settings'; +exports.examples = [ + { + title: 'Add dev menu item', + render(): React.Element { + return ( +