FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,476 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {EventConfig} from '../../../Libraries/Animated/AnimatedEvent';
import type {
AnimationConfig,
EndCallback,
} from '../../../Libraries/Animated/animations/Animation';
import type {
AnimatedNodeConfig,
EventMapping,
} from '../../../Libraries/Animated/NativeAnimatedModule';
import type {Spec as NativeAnimatedTurboModuleSpec} from '../../../Libraries/Animated/NativeAnimatedTurboModule';
import type {Node} from '../../../Libraries/Renderer/shims/ReactNativeTypes';
import type {EventSubscription} from '../../../Libraries/vendor/emitter/EventEmitter';
import NativeAnimatedNonTurboModule from '../../../Libraries/Animated/NativeAnimatedModule';
import NativeAnimatedTurboModule from '../../../Libraries/Animated/NativeAnimatedTurboModule';
import NativeEventEmitter from '../../../Libraries/EventEmitter/NativeEventEmitter';
import RCTDeviceEventEmitter from '../../../Libraries/EventEmitter/RCTDeviceEventEmitter';
import Platform from '../../../Libraries/Utilities/Platform';
import * as ReactNativeFeatureFlags from '../featureflags/ReactNativeFeatureFlags';
import invariant from 'invariant';
import nullthrows from 'nullthrows';
interface NativeAnimatedModuleSpec extends NativeAnimatedTurboModuleSpec {
// connectAnimatedNodeToShadowNodeFamily is available only in NativeAnimatedNonTurboModule
+connectAnimatedNodeToShadowNodeFamily?: (
nodeTag: number,
// $FlowExpectedError[unclear-type].
shadowNode: Object,
) => void;
}
// TODO T69437152 @petetheheat - Delete this fork when Fabric ships to 100%.
const NativeAnimatedModule: ?NativeAnimatedModuleSpec =
NativeAnimatedNonTurboModule ?? NativeAnimatedTurboModule;
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
let __nativeAnimationIdCount = 1; /* used for started animations */
let nativeEventEmitter;
const waitingForQueuedOperations = new Set<string>();
let queueOperations = false;
const queue: Array<() => void> = [];
const singleOpQueue: Array<unknown> = [];
const isSingleOpBatching =
Platform.OS === 'android' &&
NativeAnimatedModule?.queueAndExecuteBatchedOperations != null &&
ReactNativeFeatureFlags.animatedShouldUseSingleOp();
let flushQueueImmediate = null;
const eventListenerGetValueCallbacks: {
[number]: (value: number) => void,
} = {};
const eventListenerAnimationFinishedCallbacks: {
[number]: EndCallback,
} = {};
let globalEventEmitterGetValueListener: ?EventSubscription = null;
let globalEventEmitterAnimationFinishedListener: ?EventSubscription = null;
const shouldSignalBatch: boolean =
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled();
function createNativeOperations(): NonNullable<typeof NativeAnimatedModule> {
const methodNames = [
'createAnimatedNode', // 1
'updateAnimatedNodeConfig', // 2
'getValue', // 3
'startListeningToAnimatedNodeValue', // 4
'stopListeningToAnimatedNodeValue', // 5
'connectAnimatedNodes', // 6
'disconnectAnimatedNodes', // 7
'startAnimatingNode', // 8
'stopAnimation', // 9
'setAnimatedNodeValue', // 10
'setAnimatedNodeOffset', // 11
'flattenAnimatedNodeOffset', // 12
'extractAnimatedNodeOffset', // 13
'connectAnimatedNodeToView', // 14
'disconnectAnimatedNodeFromView', // 15
'restoreDefaultValues', // 16
'dropAnimatedNode', // 17
'addAnimatedEventToView', // 18
'removeAnimatedEventFromView', // 19
'addListener', // 20
'removeListener', // 21
];
if (
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled() &&
//eslint-disable-next-line
ReactNativeFeatureFlags.useSharedAnimatedBackend()
) {
methodNames.push('connectAnimatedNodeToShadowNodeFamily');
}
const nativeOperations: {
[Values<typeof methodNames>]: (...ReadonlyArray<unknown>) => void,
} = {};
if (isSingleOpBatching) {
for (let ii = 0, length = methodNames.length; ii < length; ii++) {
const methodName = methodNames[ii];
const operationID = ii + 1;
nativeOperations[methodName] = (...args) => {
// `singleOpQueue` is a flat array of operation IDs and arguments, which
// is possible because # arguments is fixed for each operation. For more
// details, see `NativeAnimatedModule.queueAndExecuteBatchedOperations`.
singleOpQueue.push(operationID, ...args);
if (shouldSignalBatch) {
clearImmediate(flushQueueImmediate);
flushQueueImmediate = setImmediate(API.flushQueue);
}
};
}
} else {
for (let ii = 0, length = methodNames.length; ii < length; ii++) {
const methodName = methodNames[ii];
nativeOperations[methodName] = (...args) => {
/* $FlowFixMe[prop-missing] Natural Inference rollout. See
* https://fburl.com/workplace/6291gfvu */
const method = nullthrows(NativeAnimatedModule)[methodName];
// If queueing is explicitly on, *or* the queue has not yet
// been flushed, use the queue. This is to prevent operations
// from being executed out of order.
if (queueOperations || queue.length !== 0) {
// $FlowExpectedError[incompatible-call] - Dynamism.
queue.push(() => method(...args));
} else if (shouldSignalBatch) {
// $FlowExpectedError[incompatible-call] - Dynamism.
queue.push(() => method(...args));
clearImmediate(flushQueueImmediate);
flushQueueImmediate = setImmediate(API.flushQueue);
} else {
// $FlowExpectedError[incompatible-call] - Dynamism.
method(...args);
}
};
}
}
// $FlowExpectedError[incompatible-type] - Dynamism.
return nativeOperations;
}
const NativeOperations = createNativeOperations();
/**
* Wrappers around NativeAnimatedModule to provide flow and autocomplete support for
* the native module methods, and automatic queue management on Android
*/
const API = {
addAnimatedEventToView(
viewTag: number,
eventName: string,
eventMapping: EventMapping,
) {
NativeOperations.addAnimatedEventToView(viewTag, eventName, eventMapping);
},
connectAnimatedNodes(parentTag: number, childTag: number): void {
NativeOperations.connectAnimatedNodes(parentTag, childTag);
},
connectAnimatedNodeToShadowNodeFamily(
nodeTag: number,
shadowNode: Node,
): void {
NativeOperations.connectAnimatedNodeToShadowNodeFamily?.(
nodeTag,
shadowNode,
);
},
connectAnimatedNodeToView(nodeTag: number, viewTag: number): void {
NativeOperations.connectAnimatedNodeToView(nodeTag, viewTag);
},
createAnimatedNode(tag: number, config: AnimatedNodeConfig): void {
if (config.disableBatchingForNativeCreate) {
NativeAnimatedModule?.createAnimatedNode(tag, config);
} else {
NativeOperations.createAnimatedNode(tag, config);
}
},
disableQueue(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
if (ReactNativeFeatureFlags.animatedShouldDebounceQueueFlush()) {
const prevImmediate = flushQueueImmediate;
clearImmediate(prevImmediate);
flushQueueImmediate = setImmediate(API.flushQueue);
} else {
API.flushQueue();
}
},
disconnectAnimatedNodeFromView(nodeTag: number, viewTag: number): void {
NativeOperations.disconnectAnimatedNodeFromView(nodeTag, viewTag);
},
disconnectAnimatedNodes(parentTag: number, childTag: number): void {
NativeOperations.disconnectAnimatedNodes(parentTag, childTag);
},
dropAnimatedNode(tag: number): void {
NativeOperations.dropAnimatedNode(tag);
},
extractAnimatedNodeOffset(nodeTag: number): void {
NativeOperations.extractAnimatedNodeOffset(nodeTag);
},
flattenAnimatedNodeOffset(nodeTag: number): void {
NativeOperations.flattenAnimatedNodeOffset(nodeTag);
},
flushQueue: (isSingleOpBatching
? (): void => {
invariant(
NativeAnimatedModule,
'Native animated module is not available',
);
flushQueueImmediate = null;
if (singleOpQueue.length === 0) {
return;
}
// Set up event listener for callbacks if it's not set up
ensureGlobalEventEmitterListeners();
// Single op batching doesn't use callback functions, instead we
// use RCTDeviceEventEmitter. This reduces overhead of sending lots of
// JSI functions across to native code; but also, TM infrastructure currently
// does not support packing a function into native arrays.
NativeAnimatedModule?.queueAndExecuteBatchedOperations?.(singleOpQueue);
singleOpQueue.length = 0;
}
: (): void => {
invariant(
NativeAnimatedModule,
'Native animated module is not available',
);
flushQueueImmediate = null;
if (queue.length === 0) {
return;
}
if (Platform.OS === 'android' || shouldSignalBatch) {
NativeAnimatedModule?.startOperationBatch?.();
}
for (let q = 0, l = queue.length; q < l; q++) {
queue[q]();
}
queue.length = 0;
if (Platform.OS === 'android' || shouldSignalBatch) {
NativeAnimatedModule?.finishOperationBatch?.();
}
}) as () => void,
getValue: (isSingleOpBatching
? (tag, saveValueCallback) => {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
if (saveValueCallback) {
eventListenerGetValueCallbacks[tag] = saveValueCallback;
}
/* $FlowExpectedError[incompatible-type] - `saveValueCallback` is handled
differently when `isSingleOpBatching` is enabled. */
NativeOperations.getValue(tag);
}
: (tag, saveValueCallback) => {
NativeOperations.getValue(tag, saveValueCallback);
}) as NonNullable<typeof NativeAnimatedModule>['getValue'],
removeAnimatedEventFromView(
viewTag: number,
eventName: string,
animatedNodeTag: number,
) {
NativeOperations.removeAnimatedEventFromView(
viewTag,
eventName,
animatedNodeTag,
);
},
restoreDefaultValues(nodeTag: number): void {
NativeOperations.restoreDefaultValues?.(nodeTag);
},
setAnimatedNodeOffset(nodeTag: number, offset: number): void {
NativeOperations.setAnimatedNodeOffset(nodeTag, offset);
},
setAnimatedNodeValue(nodeTag: number, value: number): void {
NativeOperations.setAnimatedNodeValue(nodeTag, value);
},
setWaitingForIdentifier(id: string): void {
if (shouldSignalBatch) {
return;
}
waitingForQueuedOperations.add(id);
queueOperations = true;
if (
ReactNativeFeatureFlags.animatedShouldDebounceQueueFlush() &&
flushQueueImmediate
) {
clearImmediate(flushQueueImmediate);
}
},
startAnimatingNode: (isSingleOpBatching
? (animationId, nodeTag, config, endCallback) => {
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
if (endCallback) {
eventListenerAnimationFinishedCallbacks[animationId] = endCallback;
}
/* $FlowExpectedError[incompatible-type] - `endCallback` is handled
differently when `isSingleOpBatching` is enabled. */
NativeOperations.startAnimatingNode(animationId, nodeTag, config);
}
: (animationId, nodeTag, config, endCallback) => {
NativeOperations.startAnimatingNode(
animationId,
nodeTag,
config,
endCallback,
);
}) as NonNullable<typeof NativeAnimatedModule>['startAnimatingNode'],
startListeningToAnimatedNodeValue(tag: number): void {
NativeOperations.startListeningToAnimatedNodeValue(tag);
},
stopAnimation(animationId: number) {
NativeOperations.stopAnimation(animationId);
},
stopListeningToAnimatedNodeValue(tag: number): void {
NativeOperations.stopListeningToAnimatedNodeValue(tag);
},
unsetWaitingForIdentifier(id: string): void {
if (shouldSignalBatch) {
return;
}
waitingForQueuedOperations.delete(id);
if (waitingForQueuedOperations.size === 0) {
queueOperations = false;
API.disableQueue();
}
},
updateAnimatedNodeConfig(tag: number, config: AnimatedNodeConfig): void {
NativeOperations.updateAnimatedNodeConfig?.(tag, config);
},
};
function ensureGlobalEventEmitterListeners() {
if (
globalEventEmitterGetValueListener &&
globalEventEmitterAnimationFinishedListener
) {
return;
}
globalEventEmitterGetValueListener = RCTDeviceEventEmitter.addListener(
'onNativeAnimatedModuleGetValue',
params => {
const {tag} = params;
const callback = eventListenerGetValueCallbacks[tag];
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
if (!callback) {
return;
}
callback(params.value);
delete eventListenerGetValueCallbacks[tag];
},
);
globalEventEmitterAnimationFinishedListener =
RCTDeviceEventEmitter.addListener(
'onNativeAnimatedModuleAnimationFinished',
params => {
// TODO: remove Array.isArray once native changes have propagated
const animations = Array.isArray(params) ? params : [params];
for (const animation of animations) {
const {animationId} = animation;
const callback = eventListenerAnimationFinishedCallbacks[animationId];
/* $FlowFixMe[constant-condition] Error discovered during Constant
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
if (callback) {
callback(animation);
delete eventListenerAnimationFinishedCallbacks[animationId];
}
}
},
);
}
function generateNewNodeTag(): number {
return __nativeAnimatedNodeTagCount++;
}
function generateNewAnimationId(): number {
return __nativeAnimationIdCount++;
}
function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
let _warnedMissingNativeAnimated = false;
function shouldUseNativeDriver(
config: Readonly<{...AnimationConfig, ...}> | EventConfig<unknown>,
): boolean {
if (config.useNativeDriver == null) {
console.warn(
'Animated: `useNativeDriver` was not specified. This is a required ' +
'option and must be explicitly set to `true` or `false`',
);
}
if (config.useNativeDriver === true && !NativeAnimatedModule) {
if (process.env.NODE_ENV !== 'test') {
if (!_warnedMissingNativeAnimated) {
console.warn(
'Animated: `useNativeDriver` is not supported because the native ' +
'animated module is missing. Falling back to JS-based animation. To ' +
'resolve this, add `RCTAnimation` module to this app, or remove ' +
'`useNativeDriver`. ' +
'Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md',
);
_warnedMissingNativeAnimated = true;
}
}
return false;
}
return config.useNativeDriver || false;
}
function transformDataType(value: number | string): number | string {
// Change the string type to number type so we can reuse the same logic in
// iOS and Android platform
if (typeof value !== 'string') {
return value;
}
// Normalize degrees and radians to a number expressed in radians
if (value.endsWith('deg')) {
const degrees = parseFloat(value) || 0;
return (degrees * Math.PI) / 180.0;
} else if (value.endsWith('rad')) {
return parseFloat(value) || 0;
} else {
return value;
}
}
export default {
API,
assertNativeAnimatedModule,
generateNewAnimationId,
generateNewNodeTag,
// $FlowExpectedError[unsafe-getters-setters] - unsafe getter lint suppression
// $FlowExpectedError[missing-type-arg] - unsafe getter lint suppression
get nativeEventEmitter(): NativeEventEmitter {
if (!nativeEventEmitter) {
// $FlowFixMe[underconstrained-implicit-instantiation]
nativeEventEmitter = new NativeEventEmitter(
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
// If you want to use the native module on other platforms, please remove this condition and test its behavior
Platform.OS !== 'ios' ? null : NativeAnimatedModule,
);
}
return nativeEventEmitter;
},
shouldSignalBatch,
shouldUseNativeDriver,
transformDataType,
};

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {
InterpolationConfigSupportedOutputType,
InterpolationConfigType,
} from '../../../Libraries/Animated/nodes/AnimatedInterpolation';
import {
isSupportedInterpolationParam,
isSupportedStyleProp,
isSupportedTransformProp,
} from '../../../Libraries/Animated/NativeAnimatedAllowlist';
export function validateInterpolation<
OutputT: InterpolationConfigSupportedOutputType,
>(config: InterpolationConfigType<OutputT>): void {
for (const key in config) {
if (key !== 'debugID' && !isSupportedInterpolationParam(key)) {
console.error(
`Interpolation property '${key}' is not supported by native animated module`,
);
}
}
}
export function validateStyles(styles: {[key: string]: ?number, ...}): void {
for (const key in styles) {
if (!isSupportedStyleProp(key)) {
console.error(
`Style property '${key}' is not supported by native animated module`,
);
}
}
}
export function validateTransform(
configs: Array<
| {
type: 'animated',
property: string,
nodeTag: ?number,
...
}
| {
type: 'static',
property: string,
value: number | string,
...
},
>,
): void {
configs.forEach(config => {
if (!isSupportedTransformProp(config.property)) {
console.error(
`Property '${config.property}' is not supported by native animated module`,
);
}
});
}

View File

@@ -0,0 +1,324 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {AnimatedPropsAllowlist} from '../../../Libraries/Animated/nodes/AnimatedProps';
import type {EventSubscription} from '../../../Libraries/EventEmitter/NativeEventEmitter';
import AnimatedNode from '../../../Libraries/Animated/nodes/AnimatedNode';
import AnimatedProps from '../../../Libraries/Animated/nodes/AnimatedProps';
import AnimatedValue from '../../../Libraries/Animated/nodes/AnimatedValue';
import {isPublicInstance as isFabricPublicInstance} from '../../../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstanceUtils';
import {RootTagContext} from '../../../Libraries/ReactNative/RootTag';
import useRefEffect from '../../../Libraries/Utilities/useRefEffect';
import * as ReactNativeFeatureFlags from '../featureflags/ReactNativeFeatureFlags';
import {createAnimatedPropsMemoHook} from './createAnimatedPropsMemoHook';
import NativeAnimatedHelper from './NativeAnimatedHelper';
import {
useCallback,
useContext,
useEffect,
useInsertionEffect,
useReducer,
useRef,
} from 'react';
type ReducedProps<TProps> = {
...TProps,
collapsable: boolean,
...
};
type CallbackRef<T> = T => unknown;
export type AnimatedPropsHook = <TProps: {...}, TInstance>(
props: TProps,
) => [ReducedProps<TProps>, CallbackRef<TInstance | null>];
type UpdateCallback = () => void;
type AnimatedValueListeners = Array<{
propValue: AnimatedValue,
listenerId: string,
}>;
export default function createAnimatedPropsHook(
allowlist: ?AnimatedPropsAllowlist,
): AnimatedPropsHook {
const useAnimatedPropsMemo = createAnimatedPropsMemoHook(allowlist);
const useNativePropsInFabric =
ReactNativeFeatureFlags.shouldUseSetNativePropsInFabric();
return function useAnimatedProps<TProps: {...}, TInstance>(
props: TProps,
): [ReducedProps<TProps>, CallbackRef<TInstance | null>] {
const [, scheduleUpdate] = useReducer<number, void>(count => count + 1, 0);
const onUpdateRef = useRef<UpdateCallback | null>(null);
const timerRef = useRef<TimeoutID | null>(null);
const rootTag = useContext(RootTagContext);
const node = useAnimatedPropsMemo(
() =>
new AnimatedProps(
props,
() => onUpdateRef.current?.(),
allowlist,
rootTag,
),
props,
);
useEffect(() => {
// Animated queue flush is handled deterministically in setImmediate for the following feature flags:
// cxxNativeAnimatedEnabled
if (!NativeAnimatedHelper.shouldSignalBatch) {
// If multiple components call `flushQueue`, the first one will flush the
// queue and subsequent ones will do nothing.
NativeAnimatedHelper.API.flushQueue();
}
let drivenAnimationEndedListener: ?EventSubscription = null;
if (node.__isNative) {
drivenAnimationEndedListener =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onUserDrivenAnimationEnded',
data => {
node.update();
},
);
}
return () => {
drivenAnimationEndedListener?.remove();
};
});
useAnimatedPropsLifecycle(node);
// TODO: This "effect" does three things:
//
// 1) Call `setNativeView`.
// 2) Update `onUpdateRef`.
// 3) Update listeners for `AnimatedEvent` props.
//
// Ideally, each of these would be separate "effects" so that they are not
// unnecessarily re-run when irrelevant dependencies change. For example, we
// should be able to hoist all `AnimatedEvent` props and only do #3 if either
// the `AnimatedEvent` props change or `instance` changes.
//
// But there is no way to transparently compose three separate callback refs,
// so we just combine them all into one for now.
const refEffect = useCallback(
(instance: TInstance) => {
// NOTE: This may be called more often than necessary (e.g. when `props`
// changes), but `setNativeView` already optimizes for that.
// $FlowFixMe[incompatible-type]
node.setNativeView(instance);
// NOTE: When using the JS animation driver, this callback is called on
// every animation frame. When using the native driver, this callback is
// called when the animation completes.
onUpdateRef.current = () => {
if (process.env.NODE_ENV === 'test') {
// Check 1: this is a test.
// call `scheduleUpdate` to bypass use of setNativeProps.
return scheduleUpdate();
}
const isFabricNode = isFabricInstance(instance);
if (node.__isNative) {
// Check 2: this is an animation driven by native.
// In native driven animations, this callback is only called once the animation completes.
const shouldRemoveJsSync =
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled();
if (isFabricNode && !shouldRemoveJsSync) {
// Call `scheduleUpdate` to synchronise Fiber and Shadow tree.
// Must not be called in Paper.
scheduleUpdate();
}
return;
}
if (
typeof instance !== 'object' ||
typeof instance?.setNativeProps !== 'function'
) {
// Check 3: the instance does not support setNativeProps. Call `scheduleUpdate`.
return scheduleUpdate();
}
if (!isFabricNode) {
// Check 4: this is a paper instance, call setNativeProps.
// $FlowFixMe[not-a-function] - Assume it's still a function.
// $FlowFixMe[incompatible-use]
return instance.setNativeProps(node.__getAnimatedValue());
}
if (!useNativePropsInFabric) {
// Check 5: setNativeProps are disabled.
return scheduleUpdate();
}
// This is a Fabric instance and setNativeProps is supported.
// $FlowFixMe[not-a-function] - Assume it's still a function.
// $FlowFixMe[incompatible-use]
instance.setNativeProps(node.__getAnimatedValue());
// Keeping state of Fiber tree and Shadow tree in sync.
//
// This is done by calling `scheduleUpdate` which will trigger a commit.
// However, React commit is not fast enough to drive animations.
// This is where setNativeProps comes in handy but the state between
// Fiber tree and Shadow tree needs to be kept in sync.
// The goal is to call `scheduleUpdate` as little as possible to maintain
// performance but frequently enough to keep state in sync.
// Debounce is set to 48ms, which is 3 * the duration of a frame.
// 3 frames was the highest value where flickering state was not observed.
if (timerRef.current != null) {
clearTimeout(timerRef.current);
}
timerRef.current = setTimeout(() => {
timerRef.current = null;
scheduleUpdate();
}, 48);
};
const target = getEventTarget(instance);
const animatedValueListeners: AnimatedValueListeners = [];
const eventTuples = node.__getNativeAnimatedEventTuples();
for (const [propName, propValue] of eventTuples) {
propValue.__attach(target, propName);
// $FlowFixMe[incompatible-type] - the `addListenersToPropsValue` drills down the propValue.
addListenersToPropsValue(propValue, animatedValueListeners);
}
return () => {
onUpdateRef.current = null;
for (const [propName, propValue] of eventTuples) {
propValue.__detach(target, propName);
}
for (const {propValue, listenerId} of animatedValueListeners) {
propValue.removeListener(listenerId);
}
};
},
[node],
);
const callbackRef = useRefEffect<TInstance>(refEffect);
return [reduceAnimatedProps<TProps>(node, props), callbackRef];
};
}
function reduceAnimatedProps<TProps>(
node: AnimatedProps,
props: TProps,
): ReducedProps<TProps> {
// Force `collapsable` to be false so that the native view is not flattened.
// Flattened views cannot be accurately referenced by the native driver.
return {
...node.__getValueWithStaticProps(props),
collapsable: false,
};
}
function addListenersToPropsValue(
propValue: AnimatedValue,
accumulator: AnimatedValueListeners,
) {
// propValue can be a scalar value, an array or an object.
if (propValue instanceof AnimatedValue) {
const listenerId = propValue.addListener(() => {});
accumulator.push({propValue, listenerId});
} else if (Array.isArray(propValue)) {
// An array can be an array of scalar values, arrays of arrays, or arrays of objects
for (const prop of propValue) {
addListenersToPropsValue(prop, accumulator);
}
} else if (propValue instanceof Object) {
addAnimatedValuesListenersToProps(propValue, accumulator);
}
}
function addAnimatedValuesListenersToProps(
props: AnimatedNode,
accumulator: AnimatedValueListeners,
) {
for (const propName in props) {
// $FlowFixMe[prop-missing] - This is an object contained in a prop, but we don't know the exact type.
const propValue = props[propName];
addListenersToPropsValue(propValue, accumulator);
}
}
/**
* Manages the lifecycle of the supplied `AnimatedProps` by invoking `__attach`
* and `__detach`. However, `__detach` occurs in a microtask for these reasons:
*
* 1. Optimizes detaching and attaching `AnimatedNode` instances that rely on
* reference counting to cleanup state, by causing detach to be scheduled
* after any subsequent attach.
* 2. Avoids calling `detach` during the insertion effect phase (which
* occurs during the commit phase), which may invoke completion callbacks.
*
* We should avoid invoking completion callbacks during the commit phase because
* callbacks may update state, which is unsupported and will force synchronous
* updates.
*/
function useAnimatedPropsLifecycle(node: AnimatedProps): void {
const isMounted = useRef<boolean>(false);
useInsertionEffect(() => {
isMounted.current = true;
node.__attach();
return () => {
isMounted.current = false;
queueMicrotask(() => {
// NOTE: Do not restore default values on unmount, see D18197735.
if (isMounted.current) {
// TODO: Stop restoring default values (unless `reset` is called).
node.__restoreDefaultValues();
}
node.__detach();
});
};
}, [node]);
}
function getEventTarget<TInstance>(instance: TInstance): TInstance {
return typeof instance === 'object' &&
typeof instance?.getScrollableNode === 'function'
? // $FlowFixMe[incompatible-use] - Legacy instance assumptions.
instance.getScrollableNode()
: instance;
}
// $FlowFixMe[unclear-type] - Legacy instance assumptions.
function isFabricInstance(instance: any): boolean {
return (
isFabricPublicInstance(instance) ||
// Some components have a setNativeProps function but aren't a host component
// such as lists like FlatList and SectionList. These should also use
// forceUpdate in Fabric since setNativeProps doesn't exist on the underlying
// host component. This crazy hack is essentially special casing those lists and
// ScrollView itself to use forceUpdate in Fabric.
// If these components end up using forwardRef then these hacks can go away
// as instance would actually be the underlying host component and the above check
// would be sufficient.
isFabricPublicInstance(instance?.getNativeScrollRef?.()) ||
isFabricPublicInstance(
instance?.getScrollResponder?.()?.getNativeScrollRef?.(),
)
);
}

View File

@@ -0,0 +1,360 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type AnimatedProps from '../../../Libraries/Animated/nodes/AnimatedProps';
import type {AnimatedPropsAllowlist} from '../../../Libraries/Animated/nodes/AnimatedProps';
import type {AnimatedStyleAllowlist} from '../../../Libraries/Animated/nodes/AnimatedStyle';
import {AnimatedEvent} from '../../../Libraries/Animated/AnimatedEvent';
import AnimatedNode from '../../../Libraries/Animated/nodes/AnimatedNode';
import {isPlainObject} from '../../../Libraries/Animated/nodes/AnimatedObject';
import flattenStyle from '../../../Libraries/StyleSheet/flattenStyle';
import nullthrows from 'nullthrows';
import {useInsertionEffect, useMemo, useRef} from 'react';
type CompositeKey = {
style?: {[string]: CompositeKeyComponent},
[string]:
| CompositeKeyComponent
| AnimatedEvent
| ReadonlyArray<unknown>
| Readonly<{[string]: unknown}>,
};
type CompositeKeyComponent =
| AnimatedNode
| ReadonlyArray<CompositeKeyComponent | null>
| Readonly<{[string]: CompositeKeyComponent}>;
type $ReadOnlyCompositeKey = Readonly<{
style?: Readonly<{[string]: CompositeKeyComponent}>,
[string]:
| $ReadOnlyCompositeKeyComponent
| AnimatedEvent
| ReadonlyArray<unknown>
| Readonly<{[string]: unknown}>,
}>;
type $ReadOnlyCompositeKeyComponent =
| AnimatedNode
| ReadonlyArray<$ReadOnlyCompositeKeyComponent | null>
| Readonly<{[string]: $ReadOnlyCompositeKeyComponent}>;
type AnimatedPropsMemoHook = (
() => AnimatedProps,
props: Readonly<{[string]: unknown}>,
) => AnimatedProps;
/**
* Creates a hook that returns an `AnimatedProps` object that is memoized based
* on the subset of `props` that are instances of `AnimatedNode` or
* `AnimatedEvent`.
*/
export function createAnimatedPropsMemoHook(
allowlist: ?AnimatedPropsAllowlist,
): AnimatedPropsMemoHook {
return function useAnimatedPropsMemo(
create: () => AnimatedProps,
props: Readonly<{[string]: unknown}>,
): AnimatedProps {
const compositeKey = useMemo(
() => createCompositeKeyForProps(props, allowlist),
[props],
);
const prevRef = useRef<?Readonly<{
compositeKey: typeof compositeKey,
node: AnimatedProps,
}>>();
const prev = prevRef.current;
const next =
prev != null && areCompositeKeysEqual(prev.compositeKey, compositeKey)
? prev
: {
compositeKey,
node: create(),
};
useInsertionEffect(() => {
prevRef.current = next;
}, [next]);
return next.node;
};
}
/**
* Creates a new composite key for a `props` object that can be used to detect
* whether a new `AnimatedProps` instance must be created.
*
* - With an allowlist, those props are searched for `AnimatedNode` instances.
* - Without an allowlist, `style` is searched for `AnimatedNode` instances,
* but all other objects and arrays are included (not searched). We do not
* search objects and arrays without an allowlist in case they are very large
* data structures. We safely traverse `style` becuase it is bounded.
*
* Any `AnimatedEvent` instances at the first depth are always included.
*
* If `props` contains no `AnimatedNode` or `AnimatedEvent` instances, this
* returns null.
*/
export function createCompositeKeyForProps(
props: Readonly<{[string]: unknown}>,
allowlist: ?AnimatedPropsAllowlist,
): $ReadOnlyCompositeKey | null {
let compositeKey: CompositeKey | null = null;
const keys = Object.keys(props);
for (let ii = 0, length = keys.length; ii < length; ii++) {
const key = keys[ii];
const value = props[key];
if (allowlist == null || hasOwn(allowlist, key)) {
let compositeKeyComponent;
if (key === 'style') {
// $FlowFixMe[incompatible-call] - `style` is a valid argument.
// $FlowFixMe[incompatible-type] - `flattenStyle` returns an object.
const flatStyle: ?{[string]: unknown} = flattenStyle(value);
if (flatStyle != null) {
compositeKeyComponent = createCompositeKeyForObject(
flatStyle,
allowlist?.style,
);
}
} else if (
value instanceof AnimatedNode ||
value instanceof AnimatedEvent
) {
compositeKeyComponent = value;
} else if (Array.isArray(value)) {
compositeKeyComponent =
allowlist == null ? value : createCompositeKeyForArray(value);
} else if (isPlainObject(value)) {
compositeKeyComponent =
allowlist == null ? value : createCompositeKeyForObject(value);
}
if (compositeKeyComponent != null) {
if (compositeKey == null) {
compositeKey = {} as CompositeKey;
}
compositeKey[key] = compositeKeyComponent;
}
}
}
return compositeKey;
}
/**
* Creates a new composite key for an array that retains all values that are or
* contain `AnimatedNode` instances, and `null` for the rest.
*
* If `array` contains no `AnimatedNode` instances, this returns null.
*/
function createCompositeKeyForArray(
array: ReadonlyArray<unknown>,
): ReadonlyArray<$ReadOnlyCompositeKeyComponent | null> | null {
let compositeKey: Array<$ReadOnlyCompositeKeyComponent | null> | null = null;
for (let ii = 0, length = array.length; ii < length; ii++) {
const value = array[ii];
let compositeKeyComponent;
if (value instanceof AnimatedNode) {
compositeKeyComponent = value;
} else if (Array.isArray(value)) {
compositeKeyComponent = createCompositeKeyForArray(value);
} else if (isPlainObject(value)) {
compositeKeyComponent = createCompositeKeyForObject(value);
}
if (compositeKeyComponent != null) {
if (compositeKey == null) {
compositeKey = new Array<$ReadOnlyCompositeKeyComponent | null>(
array.length,
).fill(null);
}
compositeKey[ii] = compositeKeyComponent;
}
}
return compositeKey;
}
/**
* Creates a new composite key for an object that retains only properties that
* are or contain `AnimatedNode` instances.
*
* When used to create composite keys for `style` props:
*
* - With an allowlist, those properties are searched.
* - Without an allowlist, every property is searched.
*
* If `object` contains no `AnimatedNode` instances, this returns null.
*/
function createCompositeKeyForObject(
object: Readonly<{[string]: unknown}>,
allowlist?: ?AnimatedStyleAllowlist,
): Readonly<{[string]: $ReadOnlyCompositeKeyComponent}> | null {
let compositeKey: {[string]: $ReadOnlyCompositeKeyComponent} | null = null;
const keys = Object.keys(object);
for (let ii = 0, length = keys.length; ii < length; ii++) {
const key = keys[ii];
if (allowlist == null || hasOwn(allowlist, key)) {
const value = object[key];
let compositeKeyComponent;
if (value instanceof AnimatedNode) {
compositeKeyComponent = value;
} else if (Array.isArray(value)) {
compositeKeyComponent = createCompositeKeyForArray(value);
} else if (isPlainObject(value)) {
compositeKeyComponent = createCompositeKeyForObject(value);
}
if (compositeKeyComponent != null) {
if (compositeKey == null) {
compositeKey = {} as {[string]: $ReadOnlyCompositeKeyComponent};
}
compositeKey[key] = compositeKeyComponent;
}
}
}
return compositeKey;
}
export function areCompositeKeysEqual(
maybePrev: $ReadOnlyCompositeKey | null,
maybeNext: $ReadOnlyCompositeKey | null,
allowlist: ?AnimatedPropsAllowlist,
): boolean {
if (maybePrev === maybeNext) {
return true;
}
if (maybePrev === null || maybeNext === null) {
return false;
}
// Help Flow retain the type refinements of these.
const prev = maybePrev;
const next = maybeNext;
const keys = Object.keys(prev);
const length = keys.length;
if (length !== Object.keys(next).length) {
return false;
}
for (let ii = 0; ii < length; ii++) {
const key = keys[ii];
if (!hasOwn(next, key)) {
return false;
}
const prevComponent = prev[key];
const nextComponent = next[key];
if (key === 'style') {
// We know style components are objects with non-mixed values.
if (
!areCompositeKeyComponentsEqual(
// $FlowFixMe[incompatible-type]
prevComponent as $ReadOnlyCompositeKeyComponent,
// $FlowFixMe[incompatible-type]
nextComponent as $ReadOnlyCompositeKeyComponent,
)
) {
return false;
}
} else if (
prevComponent instanceof AnimatedNode ||
prevComponent instanceof AnimatedEvent
) {
if (prevComponent !== nextComponent) {
return false;
}
} else {
// When `allowlist` is null, the components must be the same. Otherwise,
// we created the components using deep traversal, so deep compare them.
if (allowlist == null) {
if (prevComponent !== nextComponent) {
return false;
}
} else {
if (
!areCompositeKeyComponentsEqual(
// $FlowFixMe[incompatible-type]
prevComponent as $ReadOnlyCompositeKeyComponent,
// $FlowFixMe[incompatible-type]
nextComponent as $ReadOnlyCompositeKeyComponent,
)
) {
return false;
}
}
}
}
return true;
}
function areCompositeKeyComponentsEqual(
prev: $ReadOnlyCompositeKeyComponent | null,
next: $ReadOnlyCompositeKeyComponent | null,
): boolean {
if (prev === next) {
return true;
}
if (prev instanceof AnimatedNode) {
return prev === next;
}
if (Array.isArray(prev)) {
if (!Array.isArray(next)) {
return false;
}
const length = prev.length;
if (length !== next.length) {
return false;
}
for (let ii = 0; ii < length; ii++) {
if (!areCompositeKeyComponentsEqual(prev[ii], next[ii])) {
return false;
}
}
return true;
}
if (isPlainObject(prev)) {
if (!isPlainObject(next)) {
return false;
}
const keys = Object.keys(prev);
const length = keys.length;
if (length !== Object.keys(next).length) {
return false;
}
for (let ii = 0; ii < length; ii++) {
const key = keys[ii];
if (
!hasOwn(nullthrows(next), key) ||
!areCompositeKeyComponentsEqual(prev[key], next[key])
) {
return false;
}
}
return true;
}
return false;
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowFixMe[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: Readonly<{...}>, prop: string) => boolean =
// $FlowFixMe[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import View from '../../../../Libraries/Components/View/View';
import UIManager from '../../../../Libraries/ReactNative/UIManager';
import Platform from '../../../../Libraries/Utilities/Platform';
import * as React from 'react';
const exported: component(
ref?: React.RefSetter<React.ElementRef<typeof View>>,
...ViewProps
) = Platform.select({
ios: require('../../../../src/private/specs_DEPRECATED/components/RCTSafeAreaViewNativeComponent')
.default,
android: UIManager.hasViewManagerConfig('RCTSafeAreaView')
? require('../../../../src/private/specs_DEPRECATED/components/RCTSafeAreaViewNativeComponent')
.default
: View,
default: View,
});
export default exported;

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ScrollViewNativeProps} from '../../../../Libraries/Components/ScrollView/ScrollViewNativeComponentType';
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from '../../types/HostComponent';
import AndroidHorizontalScrollViewNativeComponent from '../../../../Libraries/Components/ScrollView/AndroidHorizontalScrollViewNativeComponent';
import ScrollContentViewNativeComponent from '../../../../Libraries/Components/ScrollView/ScrollContentViewNativeComponent';
import ScrollViewNativeComponent from '../../../../Libraries/Components/ScrollView/ScrollViewNativeComponent';
import Platform from '../../../../Libraries/Utilities/Platform';
import AndroidHorizontalScrollContentViewNativeComponent from '../../specs_DEPRECATED/components/AndroidHorizontalScrollContentViewNativeComponent';
export const HScrollViewNativeComponent: HostComponent<ScrollViewNativeProps> =
Platform.OS === 'android'
? AndroidHorizontalScrollViewNativeComponent
: ScrollViewNativeComponent;
export const HScrollContentViewNativeComponent: HostComponent<ViewProps> =
Platform.OS === 'android'
? AndroidHorizontalScrollContentViewNativeComponent
: ScrollContentViewNativeComponent;

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ScrollViewNativeProps} from '../../../../Libraries/Components/ScrollView/ScrollViewNativeComponentType';
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from '../../types/HostComponent';
import ScrollContentViewNativeComponent from '../../../../Libraries/Components/ScrollView/ScrollContentViewNativeComponent';
import ScrollViewNativeComponent from '../../../../Libraries/Components/ScrollView/ScrollViewNativeComponent';
import View from '../../../../Libraries/Components/View/View';
import Platform from '../../../../Libraries/Utilities/Platform';
export const VScrollViewNativeComponent: HostComponent<ScrollViewNativeProps> =
ScrollViewNativeComponent;
export const VScrollContentViewNativeComponent: HostComponent<ViewProps> =
Platform.OS === 'android' ? View : ScrollContentViewNativeComponent;

View File

@@ -0,0 +1,171 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewStyleProp} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {NativeSyntheticEvent} from '../../../../Libraries/Types/CoreEventTypes';
import type {HostInstance} from '../../types/HostInstance';
import type {NativeModeChangeEvent} from './VirtualViewNativeComponent';
import UIManager from '../../../../Libraries/ReactNative/UIManager';
import StyleSheet from '../../../../Libraries/StyleSheet/StyleSheet';
import {useVirtualViewLogging} from './logger/VirtualViewLogger';
import VirtualViewExperimentalNativeComponent from './VirtualViewExperimentalNativeComponent';
import VirtualViewProperNativeComponent from './VirtualViewNativeComponent';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {startTransition, useState} from 'react';
// @see VirtualViewNativeComponent
export enum VirtualViewMode {
Visible = 0,
Prerender = 1,
Hidden = 2,
}
// @see VirtualViewNativeComponent
export enum VirtualViewRenderState {
Unknown = 0,
Rendered = 1,
None = 2,
}
export type Rect = Readonly<{
x: number,
y: number,
width: number,
height: number,
}>;
export type ModeChangeEvent = Readonly<{
...Omit<NativeModeChangeEvent, 'mode'>,
renderState: VirtualViewRenderState,
mode: VirtualViewMode,
target: HostInstance,
}>;
// If `VirtualView` exists and `VirtualViewExperimental` does not, that means
// the new version was renamed to `VirtualView`. Eventually, this can be deleted
// with a single remaining import of `VirtualViewNativeComponent`.
const VirtualViewNativeComponent: typeof VirtualViewExperimentalNativeComponent =
UIManager.hasViewManagerConfig('VirtualView') &&
!UIManager.hasViewManagerConfig('VirtualViewExperimental')
? VirtualViewProperNativeComponent
: VirtualViewExperimentalNativeComponent;
type VirtualViewComponent = component(
children?: React.Node,
hiddenStyle?: (targetRect: Rect) => ViewStyleProp,
nativeID?: string,
ref?: ?React.RefSetter<React.ElementRef<typeof VirtualViewNativeComponent>>,
style?: ?ViewStyleProp,
onModeChange?: (event: ModeChangeEvent) => void,
removeClippedSubviews?: boolean,
);
const NotHidden = null;
type HiddenStyle = Exclude<ViewStyleProp, typeof NotHidden>;
type State = HiddenStyle | typeof NotHidden;
function defaultHiddenStyle(targetRect: Rect): ViewStyleProp {
return {minHeight: targetRect.height, minWidth: targetRect.width};
}
function createVirtualView(initialState: State): VirtualViewComponent {
const initialHidden = initialState !== NotHidden;
component VirtualView(
children?: React.Node,
hiddenStyle: (targetRect: Rect) => ViewStyleProp = defaultHiddenStyle,
nativeID?: string,
ref?: ?React.RefSetter<React.ElementRef<typeof VirtualViewNativeComponent>>,
style?: ?ViewStyleProp,
onModeChange?: (event: ModeChangeEvent) => void,
removeClippedSubviews?: boolean,
) {
const [state, setState] = useState<State>(initialState);
if (__DEV__) {
_logs.states?.push(state);
}
const isHidden = state !== NotHidden;
const loggingCallbacksRef = useVirtualViewLogging(isHidden, nativeID);
const handleModeChange = (
event: NativeSyntheticEvent<NativeModeChangeEvent>,
) => {
const mode = nullthrows(VirtualViewMode.cast(event.nativeEvent.mode));
const modeChangeEvent: ModeChangeEvent = {
mode,
renderState: isHidden
? VirtualViewRenderState.None
: VirtualViewRenderState.Rendered,
// $FlowFixMe[incompatible-type] - we know this is a HostInstance
target: event.currentTarget as HostInstance,
targetRect: event.nativeEvent.targetRect,
thresholdRect: event.nativeEvent.thresholdRect,
};
loggingCallbacksRef.current?.logModeChange(modeChangeEvent);
const emitModeChange =
onModeChange == null ? null : onModeChange.bind(null, modeChangeEvent);
match (mode) {
VirtualViewMode.Visible => {
setState(NotHidden);
emitModeChange?.();
}
VirtualViewMode.Prerender => {
startTransition(() => {
setState(NotHidden);
emitModeChange?.();
});
}
VirtualViewMode.Hidden => {
startTransition(() => {
setState(hiddenStyle(event.nativeEvent.targetRect) ?? {});
emitModeChange?.();
});
}
}
};
return (
<VirtualViewNativeComponent
initialHidden={initialHidden}
nativeID={nativeID}
ref={ref}
removeClippedSubviews={removeClippedSubviews}
renderState={
(isHidden
? VirtualViewRenderState.None
: VirtualViewRenderState.Rendered) as number
}
style={
isHidden
? StyleSheet.compose(style, nullthrows(state) as HiddenStyle)
: style
}
onModeChange={handleModeChange}>
{isHidden ? null : children}
</VirtualViewNativeComponent>
);
}
return VirtualView;
}
export default createVirtualView(NotHidden) as VirtualViewComponent;
export function createHiddenVirtualView(
style: ViewStyleProp,
): VirtualViewComponent {
return createVirtualView((style ?? {}) as HiddenStyle);
}
export const _logs: {states?: Array<State>} = {};

View File

@@ -0,0 +1,100 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {
DirectEventHandler,
Double,
Int32,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
export type NativeModeChangeEvent = Readonly<{
/**
* Virtualization mode of the target view.
*
* - `0`: Target view is visible.
* - `1`: Target view is hidden, but can be prerendered.
* - `2`: Target view is hidden.
*
* WORKAROUND: As of this writing, codegen doesn't support enums, so we need
* to convert `number` into an enum in `VirtualView`.
*/
mode: Int32,
/**
* Rect of the target view, relative to the nearest ancestor scroll container.
*/
targetRect: Readonly<{
x: Double,
y: Double,
width: Double,
height: Double,
}>,
/**
* Rect of the threshold that determines the mode of the target view, relative
* to the nearest ancestor scroll container.
*
* - `Visible`: Rect in which the target view is visible.
* - `Prerender`: Rect in which the target view is prerendered.
* - `Hidden`: Unused, without any guarantees.
*
* This can be used to determine whether and how much new content to render.
*/
thresholdRect: Readonly<{
x: Double,
y: Double,
width: Double,
height: Double,
}>,
}>;
type VirtualViewExperimentalNativeProps = Readonly<{
...ViewProps,
/**
* Whether the initial mode should be `Hidden`.
*/
initialHidden?: boolean,
/**
* This was needed to get VirtualViewManagerDelegate to set this property.
* TODO: Investigate why spread ViewProps doesn't call setter
*/
removeClippedSubviews?: boolean,
/**
* Render state of children.
*
* - `0`: Reserved to represent unknown future values.
* - `1`: Children are rendered.
* - `2`: Children are not rendered.
*
* WORKAROUND: As of this writing, codegen doesn't support enums, so we need
* to convert `number` into an enum in `VirtualView`.
*/
renderState: Int32,
/**
* See `NativeModeChangeEvent`.
*/
onModeChange?: ?DirectEventHandler<NativeModeChangeEvent>,
}>;
// TODO: Rename to eliminate "Experimental" suffix in the name.
export default codegenNativeComponent<VirtualViewExperimentalNativeProps>(
'VirtualViewExperimental',
{
interfaceOnly: true,
},
) as HostComponent<VirtualViewExperimentalNativeProps>;

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {
DirectEventHandler,
Double,
Int32,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
export type NativeModeChangeEvent = Readonly<{
/**
* Virtualization mode of the target view.
*
* - `0`: Target view is visible.
* - `1`: Target view is hidden, but can be prerendered.
* - `2`: Target view is hidden.
*
* WORKAROUND: As of this writing, codegen doesn't support enums, so we need
* to convert `number` into an enum in `VirtualView`.
*/
mode: Int32,
/**
* Rect of the target view, relative to the nearest ancestor scroll container.
*/
targetRect: Readonly<{
x: Double,
y: Double,
width: Double,
height: Double,
}>,
/**
* Rect of the threshold that determines the mode of the target view, relative
* to the nearest ancestor scroll container.
*
* - `Visible`: Rect in which the target view is visible.
* - `Prerender`: Rect in which the target view is prerendered.
* - `Hidden`: Unused, without any guarantees.
*
* This can be used to determine whether and how much new content to render.
*/
thresholdRect: Readonly<{
x: Double,
y: Double,
width: Double,
height: Double,
}>,
}>;
type VirtualViewNativeProps = Readonly<{
...ViewProps,
/**
* Whether the initial mode should be `Hidden`.
*/
initialHidden?: boolean,
/**
* This was needed to get VirtualViewManagerDelegate to set this property.
* TODO: Investigate why spread ViewProps doesn't call setter
*/
removeClippedSubviews?: boolean,
/**
* Render state of children.
*
* - `0`: Reserved to represent unknown future values.
* - `1`: Children are rendered.
* - `2`: Children are not rendered.
*
* WORKAROUND: As of this writing, codegen doesn't support enums, so we need
* to convert `number` into an enum in `VirtualView`.
*/
renderState: Int32,
/**
* See `NativeModeChangeEvent`.
*/
onModeChange?: ?DirectEventHandler<NativeModeChangeEvent>,
}>;
export default codegenNativeComponent<VirtualViewNativeProps>('VirtualView', {
interfaceOnly: true,
}) as HostComponent<VirtualViewNativeProps>;

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {IVirtualViewLogFunctions} from './VirtualViewLoggerTypes';
import {useRef} from 'react';
export hook useVirtualViewLogging(
initiallyHidden: boolean,
nativeID?: string,
): React.RefObject<?IVirtualViewLogFunctions> {
return useRef(null);
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {ModeChangeEvent} from '../../../../..';
export interface IVirtualViewLogFunctions {
logMount: () => void;
logModeChange: (event: ModeChangeEvent) => void;
logUnmount: () => void;
}
export interface IVirtualViewLogger {
getVirtualViewLoggingCallbacks(
initiallyHidden: boolean,
nativeID?: string,
): IVirtualViewLogFunctions;
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
/**
* The DevMenu module exposes methods for interacting with the Dev Menu in development.
*/
export interface DevMenuStatic {
/**
* Show the Dev Menu.
*/
show(): void;
}
export const DevMenu: DevMenuStatic;

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import NativeDevMenu from './specs/NativeDevMenu';
/**
* The DevMenu module exposes methods for interacting with the Dev Menu in development.
*/
type DevMenuStatic = {
/**
* Show the Dev Menu.
*/
show(): void,
};
const DevMenu: DevMenuStatic = {
show(): void {
if (__DEV__) {
NativeDevMenu.show?.();
}
},
};
export default DevMenu;

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
type Props = Readonly<{
children: React.Node,
box?: ?Readonly<{
top: number,
right: number,
bottom: number,
left: number,
...
}>,
style?: ViewStyleProp,
}>;
function BorderBox({children, box, style}: Props): React.Node {
if (!box) {
return children;
}
const borderStyle = {
borderTopWidth: box.top,
borderBottomWidth: box.bottom,
borderLeftWidth: box.left,
borderRightWidth: box.right,
};
return <View style={[borderStyle, style]}>{children}</View>;
}
export default BorderBox;

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {
TextStyleProp,
ViewStyleProp,
} from '../../../../../Libraries/StyleSheet/StyleSheet';
import type {InspectedElementFrame} from './Inspector';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Text = require('../../../../../Libraries/Text/Text').default;
const resolveBoxStyle = require('./resolveBoxStyle').default;
const blank = {
top: 0,
left: 0,
right: 0,
bottom: 0,
};
type BoxInspectorProps = Readonly<{
style: ViewStyleProp,
frame: ?InspectedElementFrame,
}>;
function BoxInspector({style, frame}: BoxInspectorProps): React.Node {
const margin = (style && resolveBoxStyle('margin', style)) || blank;
const padding = (style && resolveBoxStyle('padding', style)) || blank;
return (
<BoxContainer title="margin" titleStyle={styles.marginLabel} box={margin}>
<BoxContainer title="padding" box={padding}>
<View>
<Text style={styles.innerText}>
({(frame?.left || 0).toFixed(1)}, {(frame?.top || 0).toFixed(1)})
</Text>
<Text style={styles.innerText}>
{(frame?.width || 0).toFixed(1)} &times;{' '}
{(frame?.height || 0).toFixed(1)}
</Text>
</View>
</BoxContainer>
</BoxContainer>
);
}
type BoxContainerProps = Readonly<{
title: string,
titleStyle?: TextStyleProp,
box: Readonly<{
top: number,
left: number,
right: number,
bottom: number,
}>,
children: React.Node,
}>;
function BoxContainer({
title,
titleStyle,
box,
children,
}: BoxContainerProps): React.Node {
return (
<View style={styles.box}>
<View style={styles.row}>
{}
<Text style={[titleStyle, styles.label]}>{title}</Text>
<Text style={styles.boxText}>{box.top}</Text>
</View>
<View style={styles.row}>
<Text style={styles.boxText}>{box.left}</Text>
{children}
<Text style={styles.boxText}>{box.right}</Text>
</View>
<Text style={styles.boxText}>{box.bottom}</Text>
</View>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
},
marginLabel: {
width: 60,
},
label: {
fontSize: 10,
color: 'rgb(255,100,0)',
marginLeft: 5,
flex: 1,
textAlign: 'left',
top: -3,
},
innerText: {
color: 'yellow',
fontSize: 12,
textAlign: 'center',
width: 70,
},
box: {
borderWidth: 1,
borderColor: 'grey',
},
boxText: {
color: 'white',
fontSize: 12,
marginHorizontal: 3,
marginVertical: 2,
textAlign: 'center',
},
});
export default BoxInspector;

View File

@@ -0,0 +1,153 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet';
import type {InspectedElementFrame} from './Inspector';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
const flattenStyle =
require('../../../../../Libraries/StyleSheet/flattenStyle').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Dimensions =
require('../../../../../Libraries/Utilities/Dimensions').default;
const BorderBox = require('./BorderBox').default;
const resolveBoxStyle = require('./resolveBoxStyle').default;
type Props = Readonly<{
frame: InspectedElementFrame,
style?: ?ViewStyleProp,
}>;
function ElementBox({frame, style}: Props): React.Node {
const flattenedStyle = flattenStyle(style) || {};
let margin: ?Readonly<Style> = resolveBoxStyle('margin', flattenedStyle);
let padding: ?Readonly<Style> = resolveBoxStyle('padding', flattenedStyle);
const frameStyle = {...frame};
const contentStyle: {width: number, height: number} = {
width: frame.width,
height: frame.height,
};
if (margin != null) {
margin = resolveRelativeSizes(margin);
frameStyle.top -= margin.top;
frameStyle.left -= margin.left;
frameStyle.height += margin.top + margin.bottom;
frameStyle.width += margin.left + margin.right;
if (margin.top < 0) {
contentStyle.height += margin.top;
}
if (margin.bottom < 0) {
contentStyle.height += margin.bottom;
}
if (margin.left < 0) {
contentStyle.width += margin.left;
}
if (margin.right < 0) {
contentStyle.width += margin.right;
}
}
if (padding != null) {
padding = resolveRelativeSizes(padding);
contentStyle.width -= padding.left + padding.right;
contentStyle.height -= padding.top + padding.bottom;
}
return (
<View style={[styles.frame, frameStyle]} pointerEvents="none">
<BorderBox box={margin} style={styles.margin}>
<BorderBox box={padding} style={styles.padding}>
<View style={[styles.content, contentStyle]} />
</BorderBox>
</BorderBox>
</View>
);
}
const styles = StyleSheet.create({
frame: {
position: 'absolute',
},
content: {
backgroundColor: 'rgba(200, 230, 255, 0.8)', // blue
},
padding: {
borderColor: 'rgba(77, 255, 0, 0.3)', // green
},
margin: {
borderColor: 'rgba(255, 132, 0, 0.3)', // orange
},
});
type Style = {
top: number,
right: number,
bottom: number,
left: number,
...
};
/**
* Resolves relative sizes (percentages and auto) in a style object.
*
* @param style the style to resolve
* @return a modified copy
*/
function resolveRelativeSizes(style: Readonly<Style>): Style {
let resolvedStyle = {...style};
resolveSizeInPlace(resolvedStyle, 'top', 'height');
resolveSizeInPlace(resolvedStyle, 'right', 'width');
resolveSizeInPlace(resolvedStyle, 'bottom', 'height');
resolveSizeInPlace(resolvedStyle, 'left', 'width');
return resolvedStyle;
}
/**
* Resolves the given size of a style object in place.
*
* @param style the style object to modify
* @param direction the direction to resolve (e.g. 'top')
* @param dimension the window dimension that this direction belongs to (e.g. 'height')
*/
function resolveSizeInPlace(
style: Style,
direction: string,
dimension: string,
) {
// $FlowFixMe[invalid-computed-prop]
if (style[direction] !== null && typeof style[direction] === 'string') {
if (style[direction].indexOf('%') !== -1) {
// $FlowFixMe[prop-missing]
style[direction] =
// $FlowFixMe[invalid-computed-prop]
(parseFloat(style[direction]) / 100.0) *
// $FlowFixMe[invalid-computed-prop]
Dimensions.get('window')[dimension];
}
// $FlowFixMe[invalid-computed-prop]
if (style[direction] === 'auto') {
// Ignore auto sizing in frame drawing due to complexity of correctly rendering this
// $FlowFixMe[prop-missing]
style[direction] = 0;
}
}
}
export default ElementBox;

View File

@@ -0,0 +1,126 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {InspectorData} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes';
import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet';
import * as React from 'react';
const TouchableHighlight =
require('../../../../../Libraries/Components/Touchable/TouchableHighlight').default;
const TouchableWithoutFeedback =
require('../../../../../Libraries/Components/Touchable/TouchableWithoutFeedback').default;
const View = require('../../../../../Libraries/Components/View/View').default;
const flattenStyle =
require('../../../../../Libraries/StyleSheet/flattenStyle').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Text = require('../../../../../Libraries/Text/Text').default;
const mapWithSeparator =
require('../../../../../Libraries/Utilities/mapWithSeparator').default;
const BoxInspector = require('./BoxInspector').default;
const StyleInspector = require('./StyleInspector').default;
type Props = Readonly<{
hierarchy: ?InspectorData['hierarchy'],
style?: ?ViewStyleProp,
frame?: ?Object,
selection?: ?number,
setSelection?: number => unknown,
}>;
class ElementProperties extends React.Component<Props> {
render(): React.Node {
const style = flattenStyle(this.props.style);
const selection = this.props.selection;
// Without the `TouchableWithoutFeedback`, taps on this inspector pane
// would change the inspected element to whatever is under the inspector
return (
<TouchableWithoutFeedback>
<View style={styles.info}>
<View style={styles.breadcrumb}>
{this.props.hierarchy != null &&
mapWithSeparator(
this.props.hierarchy,
(hierarchyItem, i): React.MixedElement => (
<TouchableHighlight
key={'item-' + i}
style={[
styles.breadItem,
i === selection && styles.selected,
]}
// $FlowFixMe[not-a-function] found when converting React.createClass to ES6
onPress={() => this.props.setSelection(i)}>
<Text style={styles.breadItemText}>
{hierarchyItem.name}
</Text>
</TouchableHighlight>
),
(i): React.MixedElement => (
<Text key={'sep-' + i} style={styles.breadSep}>
&#9656;
</Text>
),
)}
</View>
<View style={styles.row}>
<View style={styles.col}>
<StyleInspector style={style} />
</View>
<BoxInspector style={style} frame={this.props.frame} />
</View>
</View>
</TouchableWithoutFeedback>
);
}
}
const styles = StyleSheet.create({
breadSep: {
fontSize: 8,
color: 'white',
},
breadcrumb: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'flex-start',
marginBottom: 5,
},
selected: {
borderColor: 'white',
borderRadius: 5,
},
breadItem: {
borderWidth: 1,
borderColor: 'transparent',
marginHorizontal: 2,
},
breadItemText: {
fontSize: 10,
color: 'white',
marginHorizontal: 5,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
col: {
flex: 1,
},
info: {
padding: 10,
},
});
export default ElementProperties;

View File

@@ -0,0 +1,214 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {InspectedViewRef} from '../../../../../Libraries/ReactNative/AppContainer-dev';
import type {
InspectorData,
TouchedViewDataAtPoint,
} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes';
import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet';
import type {ReactDevToolsAgent} from '../../../../../Libraries/Types/ReactDevToolsTypes';
import SafeAreaView from '../../../components/safeareaview/SafeAreaView_INTERNAL_DO_NOT_USE';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
const PressabilityDebug = require('../../../../../Libraries/Pressability/PressabilityDebug');
const {
findNodeHandle,
} = require('../../../../../Libraries/ReactNative/RendererProxy');
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Dimensions =
require('../../../../../Libraries/Utilities/Dimensions').default;
const Platform = require('../../../../../Libraries/Utilities/Platform').default;
const getInspectorDataForViewAtPoint =
require('./getInspectorDataForViewAtPoint').default;
const InspectorOverlay = require('./InspectorOverlay').default;
const InspectorPanel = require('./InspectorPanel').default;
const {useState} = React;
type PanelPosition = 'top' | 'bottom';
export type InspectedElementFrame = TouchedViewDataAtPoint['frame'];
export type InspectedElement = Readonly<{
frame: InspectedElementFrame,
style?: ViewStyleProp,
}>;
export type ElementsHierarchy = InspectorData['hierarchy'];
type ExternalInspection = {
externalInspectingEnabled: boolean,
+reportToExternalInspection: (viewData: TouchedViewDataAtPoint) => void,
};
type Props = {
inspectedViewRef: InspectedViewRef,
onRequestRerenderApp: () => void,
reactDevToolsAgent?: ReactDevToolsAgent,
devMenuInspectorOpen: boolean,
externalInspection: ExternalInspection,
};
function Inspector({
inspectedViewRef,
onRequestRerenderApp,
reactDevToolsAgent,
devMenuInspectorOpen,
externalInspection,
}: Props): React.Node {
const [inspectingEnabled, setInspectingEnabled] = useState<boolean>(true);
const {externalInspectingEnabled, reportToExternalInspection} =
externalInspection;
const [panelPosition, setPanelPosition] = useState<PanelPosition>('bottom');
const [inspectedElement, setInspectedElement] =
useState<?InspectedElement>(null);
const [selectionIndex, setSelectionIndex] = useState<?number>(null);
const [elementsHierarchy, setElementsHierarchy] =
useState<?ElementsHierarchy>(null);
// Derive inspecting state: external inspection forces it on, otherwise use local state
const isInspecting = externalInspectingEnabled || inspectingEnabled;
const setSelection = (i: number) => {
const hierarchyItem = elementsHierarchy?.[i];
if (hierarchyItem == null) {
return;
}
// We pass in findNodeHandle as the method is injected
const {measure, props} = hierarchyItem.getInspectorData(findNodeHandle);
measure((x, y, width, height, left, top) => {
// $FlowFixMe[incompatible-type] `props` from InspectorData are defined as <string, string> dictionary, which is incompatible with ViewStyleProp
setInspectedElement({
frame: {left, top, width, height},
style: props.style,
});
setSelectionIndex(i);
});
};
const onTouchPoint = (locationX: number, locationY: number) => {
const setTouchedViewData = (viewData: TouchedViewDataAtPoint) => {
const {
hierarchy,
props,
selectedIndex,
frame,
pointerY,
touchedViewTag,
closestInstance,
} = viewData;
// Report to external inspection if in external inspection mode
if (externalInspectingEnabled) {
reportToExternalInspection(viewData);
}
// Sync the touched view with React DevTools.
// Note: This is Paper only. To support Fabric,
// DevTools needs to be updated to not rely on view tags.
if (reactDevToolsAgent) {
reactDevToolsAgent.selectNode(findNodeHandle(touchedViewTag));
if (closestInstance != null) {
reactDevToolsAgent.selectNode(closestInstance);
}
}
setPanelPosition(
pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom',
);
setSelectionIndex(selectedIndex);
setElementsHierarchy(hierarchy);
// $FlowFixMe[incompatible-type] `props` from InspectorData are defined as <string, string> dictionary, which is incompatible with ViewStyleProp
setInspectedElement({
frame,
style: props.style,
});
};
getInspectorDataForViewAtPoint(
inspectedViewRef.current,
locationX,
locationY,
viewData => {
setTouchedViewData(viewData);
return false;
},
);
};
const handleSetInspecting = (enabled: boolean) => {
setInspectingEnabled(enabled);
setInspectedElement(null);
};
const setTouchTargeting = (val: boolean) => {
PressabilityDebug.setEnabled(val);
onRequestRerenderApp();
};
const panelContainerStyle =
panelPosition === 'bottom'
? {bottom: 0}
: Platform.select({ios: {top: 0}, default: {top: 0}});
return (
<View style={styles.container} pointerEvents="box-none">
{isInspecting && (
<InspectorOverlay
inspected={inspectedElement}
onTouchPoint={onTouchPoint}
externalInspectionActive={externalInspectingEnabled}
/>
)}
{!externalInspectingEnabled && (
<SafeAreaView style={[styles.panelContainer, panelContainerStyle]}>
<InspectorPanel
devtoolsIsOpen={!!reactDevToolsAgent}
inspecting={inspectingEnabled}
setInspecting={handleSetInspecting}
inspected={inspectedElement}
hierarchy={elementsHierarchy}
selection={selectionIndex}
setSelection={setSelection}
touchTargeting={PressabilityDebug.isEnabled()}
setTouchTargeting={setTouchTargeting}
/>
</SafeAreaView>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
backgroundColor: 'transparent',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
panelContainer: {
position: 'absolute',
left: 0,
right: 0,
},
});
export default Inspector;

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
import type {GestureResponderEvent} from '../../../../../Libraries/Types/CoreEventTypes';
import type {InspectedElement} from './Inspector';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const ElementBox = require('./ElementBox').default;
type Props = Readonly<{
inspected?: ?InspectedElement,
onTouchPoint: (locationX: number, locationY: number) => void,
externalInspectionActive?: boolean,
}>;
function InspectorOverlay({
inspected,
onTouchPoint,
externalInspectionActive,
}: Props): React.Node {
const findViewForTouchEvent = (e: GestureResponderEvent) => {
const {locationX, locationY} = e.nativeEvent.touches[0];
onTouchPoint(locationX, locationY);
};
const handleStartShouldSetResponder = (e: GestureResponderEvent): boolean => {
findViewForTouchEvent(e);
return true;
};
let content = null;
if (inspected) {
content = <ElementBox frame={inspected.frame} style={inspected.style} />;
}
return (
<View
onStartShouldSetResponder={handleStartShouldSetResponder}
onResponderMove={findViewForTouchEvent}
nativeID="inspectorOverlay" /* TODO: T68258846. */
style={[
styles.inspector,
externalInspectionActive === true && styles.externalInspectionIndicator,
]}>
{content}
</View>
);
}
const styles = StyleSheet.create({
inspector: {
backgroundColor: 'transparent',
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
externalInspectionIndicator: {
borderWidth: 4,
borderColor: 'rgba(255, 0, 0, 0.8)',
},
});
export default InspectorOverlay;

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {ElementsHierarchy, InspectedElement} from './Inspector';
import SafeAreaView from '../../../../../Libraries/Components/SafeAreaView/SafeAreaView';
import * as React from 'react';
const ScrollView =
require('../../../../../Libraries/Components/ScrollView/ScrollView').default;
const TouchableHighlight =
require('../../../../../Libraries/Components/Touchable/TouchableHighlight').default;
const View = require('../../../../../Libraries/Components/View/View').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Text = require('../../../../../Libraries/Text/Text').default;
const ElementProperties = require('./ElementProperties').default;
type Props = Readonly<{
devtoolsIsOpen: boolean,
inspecting: boolean,
setInspecting: (val: boolean) => void,
touchTargeting: boolean,
setTouchTargeting: (val: boolean) => void,
hierarchy?: ?ElementsHierarchy,
selection?: ?number,
setSelection: number => unknown,
inspected?: ?InspectedElement,
}>;
class InspectorPanel extends React.Component<Props> {
renderWaiting(): React.Node {
if (this.props.inspecting) {
return (
<Text style={styles.waitingText}>Tap something to inspect it</Text>
);
}
return <Text style={styles.waitingText}>Nothing is inspected</Text>;
}
render(): React.Node {
let contents;
if (this.props.inspected) {
contents = (
<ScrollView style={styles.properties}>
<ElementProperties
style={this.props.inspected.style}
frame={this.props.inspected.frame}
hierarchy={this.props.hierarchy}
selection={this.props.selection}
setSelection={this.props.setSelection}
/>
</ScrollView>
);
} else {
contents = <View style={styles.waiting}>{this.renderWaiting()}</View>;
}
return (
<SafeAreaView style={styles.container}>
{!this.props.devtoolsIsOpen && contents}
<View style={styles.buttonRow}>
<InspectorPanelButton
title={'Inspect'}
pressed={this.props.inspecting}
onClick={this.props.setInspecting}
/>
<InspectorPanelButton
title={'Touchables'}
pressed={this.props.touchTargeting}
onClick={this.props.setTouchTargeting}
/>
</View>
</SafeAreaView>
);
}
}
type InspectorPanelButtonProps = Readonly<{
onClick: (val: boolean) => void,
pressed: boolean,
title: string,
}>;
class InspectorPanelButton extends React.Component<InspectorPanelButtonProps> {
render(): React.Node {
return (
<TouchableHighlight
onPress={() => this.props.onClick(!this.props.pressed)}
style={[styles.button, this.props.pressed && styles.buttonPressed]}>
<Text style={styles.buttonText}>{this.props.title}</Text>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
buttonRow: {
flexDirection: 'row',
},
button: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
margin: 2,
height: 30,
justifyContent: 'center',
alignItems: 'center',
},
buttonPressed: {
backgroundColor: 'rgba(255, 255, 255, 0.3)',
},
buttonText: {
textAlign: 'center',
color: 'white',
margin: 5,
},
container: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
},
properties: {
height: 200,
},
waiting: {
height: 100,
},
waitingText: {
fontSize: 20,
textAlign: 'center',
marginVertical: 20,
color: 'white',
},
});
export default InspectorPanel;

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {InspectedViewRef} from '../../../../../Libraries/ReactNative/AppContainer-dev';
import type {PointerEvent} from '../../../../../Libraries/Types/CoreEventTypes';
import type {GestureResponderEvent} from '../../../../../Libraries/Types/CoreEventTypes';
import type {ReactDevToolsAgent} from '../../../../../Libraries/Types/ReactDevToolsTypes';
import type {InspectedElement} from './Inspector';
import View from '../../../../../Libraries/Components/View/View';
import StyleSheet from '../../../../../Libraries/StyleSheet/StyleSheet';
import * as ReactNativeFeatureFlags from '../../../../../src/private/featureflags/ReactNativeFeatureFlags';
import ElementBox from './ElementBox';
import * as React from 'react';
const getInspectorDataForViewAtPoint =
require('./getInspectorDataForViewAtPoint').default;
const {useEffect, useState, useCallback} = React;
type Props = {
inspectedViewRef: InspectedViewRef,
reactDevToolsAgent: ReactDevToolsAgent,
};
export default function ReactDevToolsOverlay({
inspectedViewRef,
reactDevToolsAgent,
}: Props): React.Node {
const [inspected, setInspected] = useState<?InspectedElement>(null);
const [isInspecting, setIsInspecting] = useState(false);
useEffect(() => {
function cleanup() {
reactDevToolsAgent.removeListener('shutdown', cleanup);
reactDevToolsAgent.removeListener(
'startInspectingNative',
onStartInspectingNative,
);
reactDevToolsAgent.removeListener(
'stopInspectingNative',
onStopInspectingNative,
);
}
function onStartInspectingNative() {
setIsInspecting(true);
setInspected(null);
}
function onStopInspectingNative() {
setIsInspecting(false);
setInspected(null);
}
reactDevToolsAgent.addListener('shutdown', cleanup);
reactDevToolsAgent.addListener(
'startInspectingNative',
onStartInspectingNative,
);
reactDevToolsAgent.addListener(
'stopInspectingNative',
onStopInspectingNative,
);
return cleanup;
}, [reactDevToolsAgent]);
const findViewForLocation = useCallback(
(x: number, y: number) => {
getInspectorDataForViewAtPoint(
inspectedViewRef.current,
x,
y,
viewData => {
const {frame, closestPublicInstance} = viewData;
if (closestPublicInstance == null) {
return false;
}
reactDevToolsAgent.selectNode(closestPublicInstance);
setInspected({frame});
return true;
},
);
},
[inspectedViewRef, reactDevToolsAgent],
);
const onPointerMove = useCallback(
(e: PointerEvent) => {
findViewForLocation(e.nativeEvent.x, e.nativeEvent.y);
},
[findViewForLocation],
);
const onResponderMove = useCallback(
(e: GestureResponderEvent) => {
findViewForLocation(
e.nativeEvent.touches[0].locationX,
e.nativeEvent.touches[0].locationY,
);
},
[findViewForLocation],
);
const shouldSetResponder = useCallback(
(e: GestureResponderEvent): boolean => {
onResponderMove(e);
return true;
},
[onResponderMove],
);
const highlight = inspected ? <ElementBox frame={inspected.frame} /> : null;
if (isInspecting) {
const events =
// Pointer events only work on fabric
ReactNativeFeatureFlags.shouldPressibilityUseW3CPointerEventsForHover()
? {
onPointerMove,
onPointerDown: onPointerMove,
}
: {
onStartShouldSetResponder: shouldSetResponder,
onResponderMove: onResponderMove,
};
return (
<View
nativeID="devToolsInspectorOverlay"
style={styles.inspector}
{...events}>
{highlight}
</View>
);
}
return highlight;
}
const styles = StyleSheet.create({
inspector: {
backgroundColor: 'transparent',
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
});

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {ViewStyleProp} from '../../../../../Libraries/StyleSheet/StyleSheet';
import type {____FlattenStyleProp_Internal} from '../../../../../Libraries/StyleSheet/StyleSheetTypes';
import * as React from 'react';
const View = require('../../../../../Libraries/Components/View/View').default;
const StyleSheet =
require('../../../../../Libraries/StyleSheet/StyleSheet').default;
const Text = require('../../../../../Libraries/Text/Text').default;
type Props = Readonly<{
style?: ?____FlattenStyleProp_Internal<ViewStyleProp>,
}>;
function StyleInspector({style}: Props): React.Node {
if (!style) {
return <Text style={styles.noStyle}>No style</Text>;
}
const names = Object.keys(style);
return (
<View style={styles.container}>
<View>
{names.map(name => (
<Text key={name} style={styles.attr}>
{name}:
</Text>
))}
</View>
<View>
{names.map(name => {
const value = style?.[name];
return (
<Text key={name} style={styles.value}>
{typeof value !== 'string' && typeof value !== 'number'
? JSON.stringify(value)
: value}
</Text>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
},
attr: {
fontSize: 10,
color: '#ccc',
},
value: {
fontSize: 10,
color: 'white',
marginLeft: 10,
},
noStyle: {
color: 'white',
fontSize: 10,
},
});
export default StyleInspector;

View File

@@ -0,0 +1,218 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import typeof XMLHttpRequestT from '../../../../../Libraries/Network/XMLHttpRequest';
const XMLHttpRequest: XMLHttpRequestT =
require('../../../../../Libraries/Network/XMLHttpRequest').default;
// $FlowFixMe[method-unbinding]
const originalXHROpen = XMLHttpRequest.prototype.open;
// $FlowFixMe[method-unbinding]
const originalXHRSend = XMLHttpRequest.prototype.send;
// $FlowFixMe[method-unbinding]
const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
type XHRInterceptorOpenCallback = (
method: string,
url: string,
request: XMLHttpRequest,
) => void;
type XHRInterceptorSendCallback = (
data: string,
request: XMLHttpRequest,
) => void;
type XHRInterceptorRequestHeaderCallback = (
header: string,
value: string,
request: XMLHttpRequest,
) => void;
type XHRInterceptorHeaderReceivedCallback = (
responseContentType: string | void,
responseSize: number | void,
allHeaders: string,
request: XMLHttpRequest,
) => void;
type XHRInterceptorResponseCallback = (
status: number,
timeout: number,
response: string,
responseURL: string,
responseType: string,
request: XMLHttpRequest,
) => void;
let openCallback: XHRInterceptorOpenCallback | null;
let sendCallback: XHRInterceptorSendCallback | null;
let requestHeaderCallback: XHRInterceptorRequestHeaderCallback | null;
let headerReceivedCallback: XHRInterceptorHeaderReceivedCallback | null;
let responseCallback: XHRInterceptorResponseCallback | null;
let isInterceptorEnabled = false;
/**
* A network interceptor which monkey-patches XMLHttpRequest methods
* to gather all network requests/responses.
* This supports interception with XMLHttpRequest API, including Fetch API
* and any other third party libraries that depend on XMLHttpRequest.
*
* @deprecated Since React Native 0.84
*/
const XHRInterceptor = {
/**
* Invoked before XMLHttpRequest.open(...) is called.
*/
setOpenCallback(callback: XHRInterceptorOpenCallback) {
openCallback = callback;
},
/**
* Invoked before XMLHttpRequest.send(...) is called.
*/
setSendCallback(callback: XHRInterceptorSendCallback) {
sendCallback = callback;
},
/**
* Invoked after xhr's readyState becomes xhr.HEADERS_RECEIVED.
*/
setHeaderReceivedCallback(callback: XHRInterceptorHeaderReceivedCallback) {
headerReceivedCallback = callback;
},
/**
* Invoked after xhr's readyState becomes xhr.DONE.
*/
setResponseCallback(callback: XHRInterceptorResponseCallback) {
responseCallback = callback;
},
/**
* Invoked before XMLHttpRequest.setRequestHeader(...) is called.
*/
setRequestHeaderCallback(callback: XHRInterceptorRequestHeaderCallback) {
requestHeaderCallback = callback;
},
isInterceptorEnabled(): boolean {
return isInterceptorEnabled;
},
enableInterception() {
if (isInterceptorEnabled) {
return;
}
// Override `open` method for all XHR requests to intercept the request
// method and url, then pass them through the `openCallback`.
// $FlowFixMe[cannot-write]
// $FlowFixMe[missing-this-annot]
XMLHttpRequest.prototype.open = function (method: string, url: string) {
if (openCallback) {
openCallback(method, url, this);
}
originalXHROpen.apply(this, arguments);
};
// Override `setRequestHeader` method for all XHR requests to intercept
// the request headers, then pass them through the `requestHeaderCallback`.
// $FlowFixMe[cannot-write]
// $FlowFixMe[missing-this-annot]
XMLHttpRequest.prototype.setRequestHeader = function (
header: string,
value: string,
) {
if (requestHeaderCallback) {
requestHeaderCallback(header, value, this);
}
originalXHRSetRequestHeader.apply(this, arguments);
};
// Override `send` method of all XHR requests to intercept the data sent,
// register listeners to intercept the response, and invoke the callbacks.
// $FlowFixMe[cannot-write]
// $FlowFixMe[missing-this-annot]
XMLHttpRequest.prototype.send = function (data: string) {
if (sendCallback) {
sendCallback(data, this);
}
if (this.addEventListener) {
this.addEventListener(
'readystatechange',
() => {
if (!isInterceptorEnabled) {
return;
}
if (this.readyState === this.HEADERS_RECEIVED) {
const contentTypeString = this.getResponseHeader('Content-Type');
const contentLengthString =
this.getResponseHeader('Content-Length');
let responseContentType, responseSize;
if (contentTypeString) {
responseContentType = contentTypeString.split(';')[0];
}
if (contentLengthString) {
responseSize = parseInt(contentLengthString, 10);
}
if (headerReceivedCallback) {
headerReceivedCallback(
responseContentType,
responseSize,
this.getAllResponseHeaders(),
this,
);
}
}
if (this.readyState === this.DONE) {
if (responseCallback) {
responseCallback(
this.status,
this.timeout,
this.response,
this.responseURL,
this.responseType,
this,
);
}
}
},
false,
);
}
originalXHRSend.apply(this, arguments);
};
isInterceptorEnabled = true;
},
// Unpatch XMLHttpRequest methods and remove the callbacks.
disableInterception() {
if (!isInterceptorEnabled) {
return;
}
isInterceptorEnabled = false;
// $FlowFixMe[cannot-write]
XMLHttpRequest.prototype.send = originalXHRSend;
// $FlowFixMe[cannot-write]
XMLHttpRequest.prototype.open = originalXHROpen;
// $FlowFixMe[cannot-write]
XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
responseCallback = null;
openCallback = null;
sendCallback = null;
headerReceivedCallback = null;
requestHeaderCallback = null;
},
};
export default XHRInterceptor;

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TouchedViewDataAtPoint} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes';
import type {HostInstance} from '../../../types/HostInstance';
const invariant = require('invariant');
export type ReactRenderer = {
rendererConfig: {
getInspectorDataForViewAtPoint: (
inspectedView: ?HostInstance,
locationX: number,
locationY: number,
callback: Function,
) => void,
...
},
};
type AttachedRendererEventPayload = {id: number, renderer: ReactRenderer};
const reactDevToolsHook = (window: any).__REACT_DEVTOOLS_GLOBAL_HOOK__;
invariant(
Boolean(reactDevToolsHook),
'getInspectorDataForViewAtPoint should not be used if React DevTools hook is not injected',
);
const renderers: Array<ReactRenderer> = Array.from(
(window: any).__REACT_DEVTOOLS_GLOBAL_HOOK__.renderers.values(),
);
const appendRenderer = ({renderer}: AttachedRendererEventPayload) =>
renderers.push(renderer);
reactDevToolsHook.on('renderer', appendRenderer);
function validateRenderers(): void {
invariant(
renderers.length > 0,
'Expected to find at least one React Native renderer on DevTools hook.',
);
}
function getInspectorDataForViewAtPoint(
inspectedView: ?HostInstance,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => boolean,
) {
validateRenderers();
let shouldBreak = false;
// Check all renderers for inspector data.
for (const renderer of renderers) {
if (shouldBreak) {
break;
}
if (renderer?.rendererConfig?.getInspectorDataForViewAtPoint != null) {
renderer.rendererConfig.getInspectorDataForViewAtPoint(
inspectedView,
locationX,
locationY,
viewData => {
// Only return with non-empty view data since only one renderer will have this view.
if (viewData && viewData.hierarchy.length > 0) {
shouldBreak = callback(viewData);
}
},
);
}
}
}
export default getInspectorDataForViewAtPoint;

View File

@@ -0,0 +1,115 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const I18nManager =
require('../../../../../Libraries/ReactNative/I18nManager').default;
/**
* Resolve a style property into its component parts.
*
* For example:
*
* > resolveProperties('margin', {margin: 5, marginBottom: 10})
* {top: 5, left: 5, right: 5, bottom: 10}
*
* If no parts exist, this returns null.
*/
function resolveBoxStyle(
prefix: string,
style: Object,
): ?Readonly<{
bottom: number,
left: number,
right: number,
top: number,
}> {
let hasParts = false;
const result = {
bottom: 0,
left: 0,
right: 0,
top: 0,
};
// TODO: Fix issues with multiple properties affecting the same side.
const styleForAll = style[prefix];
if (styleForAll != null) {
for (const key of Object.keys(result)) {
result[key] = styleForAll;
}
hasParts = true;
}
const styleForHorizontal = style[prefix + 'Horizontal'];
if (styleForHorizontal != null) {
result.left = styleForHorizontal;
result.right = styleForHorizontal;
hasParts = true;
} else {
const styleForLeft = style[prefix + 'Left'];
if (styleForLeft != null) {
result.left = styleForLeft;
hasParts = true;
}
const styleForRight = style[prefix + 'Right'];
if (styleForRight != null) {
result.right = styleForRight;
hasParts = true;
}
const styleForEnd = style[prefix + 'End'];
if (styleForEnd != null) {
const constants = I18nManager.getConstants();
if (constants.isRTL && constants.doLeftAndRightSwapInRTL) {
result.left = styleForEnd;
} else {
result.right = styleForEnd;
}
hasParts = true;
}
const styleForStart = style[prefix + 'Start'];
if (styleForStart != null) {
const constants = I18nManager.getConstants();
if (constants.isRTL && constants.doLeftAndRightSwapInRTL) {
result.right = styleForStart;
} else {
result.left = styleForStart;
}
hasParts = true;
}
}
const styleForVertical = style[prefix + 'Vertical'];
if (styleForVertical != null) {
result.bottom = styleForVertical;
result.top = styleForVertical;
hasParts = true;
} else {
const styleForBottom = style[prefix + 'Bottom'];
if (styleForBottom != null) {
result.bottom = styleForBottom;
hasParts = true;
}
const styleForTop = style[prefix + 'Top'];
if (styleForTop != null) {
result.top = styleForTop;
hasParts = true;
}
}
return hasParts ? result : null;
}
export default resolveBoxStyle;

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TouchedViewDataAtPoint} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes';
import * as ReactNativeFeatureFlags from '../../../../../src/private/featureflags/ReactNativeFeatureFlags';
import {useCallback, useEffect, useState} from 'react';
type ExternalInspectionAPI = {
enable: () => void,
disable: () => void,
};
declare var __EXTERNAL_INSPECTION__: ?ExternalInspectionAPI;
declare var __EXTERNAL_INSPECTION_SELECT__: ?(payload: string) => void;
type UseExternalInspectionResult = {
externalInspectingEnabled: boolean,
reportToExternalInspection: (viewData: TouchedViewDataAtPoint) => void,
};
/**
* Initializes the __EXTERNAL_INSPECTION__ API on the device.
*/
function ensureExternalInspectionAPI(
onEnable: () => void,
onDisable: () => void,
): ExternalInspectionAPI {
if (
typeof __EXTERNAL_INSPECTION__ === 'undefined' ||
__EXTERNAL_INSPECTION__ == null
) {
const api: ExternalInspectionAPI = {
enable: onEnable,
disable: onDisable,
};
// $FlowFixMe[prop-missing] Initializing global API for DevTools communication
(global: $FlowFixMe).__EXTERNAL_INSPECTION__ = api;
return api;
}
__EXTERNAL_INSPECTION__.enable = onEnable;
__EXTERNAL_INSPECTION__.disable = onDisable;
return __EXTERNAL_INSPECTION__;
}
export default function useExternalInspection(): UseExternalInspectionResult {
const [externalInspectingEnabled, setExternalInspectingEnabled] =
useState<boolean>(false);
useEffect(() => {
if (!ReactNativeFeatureFlags.externalElementInspectionEnabled()) {
return;
}
const handleEnable = () => {
setExternalInspectingEnabled(true);
};
const handleDisable = () => {
setExternalInspectingEnabled(false);
};
ensureExternalInspectionAPI(handleEnable, handleDisable);
}, []);
const reportToExternalInspection = useCallback(
(viewData: TouchedViewDataAtPoint) => {
if (
typeof __EXTERNAL_INSPECTION_SELECT__ === 'undefined' ||
__EXTERNAL_INSPECTION_SELECT__ === null ||
!externalInspectingEnabled
) {
return;
}
__EXTERNAL_INSPECTION_SELECT__(
JSON.stringify({
frame: viewData.frame,
hierarchy: viewData.hierarchy.map(item => ({
name: item.name,
})),
touchedViewTag: viewData.touchedViewTag,
}),
);
},
[externalInspectingEnabled],
);
return {
externalInspectingEnabled,
reportToExternalInspection,
};
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+show: () => void;
+reload: () => void;
+setProfilingEnabled: (enabled: boolean) => void;
+setHotLoadingEnabled: (enabled: boolean) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('DevMenu'): Spec);

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import GlobalStateObserver from './GlobalStateObserver';
const observer = new GlobalStateObserver(
'__DEBUGGER_SESSION_OBSERVER__',
'hasActiveSession',
);
const FuseboxSessionObserver = {
hasActiveSession(): boolean {
return observer.getStatus();
},
subscribe(callback: (status: boolean) => void): () => void {
return observer.subscribe(callback);
},
};
export default FuseboxSessionObserver;

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
/**
* Generic observer for a boolean state exposed via a native global object.
*
* Native code installs a global object with the following shape:
* global[globalName] = {
* [statusProperty]: boolean,
* subscribers: Set<(status: boolean) => void>,
* [callbackName]: (status: boolean) => void,
* }
*
* This class provides a JS-friendly API over that global object.
*/
class GlobalStateObserver {
#globalName: string;
#statusProperty: string;
constructor(globalName: string, statusProperty: string) {
this.#globalName = globalName;
this.#statusProperty = statusProperty;
}
#hasNativeSupport(): boolean {
return global.hasOwnProperty(this.#globalName);
}
getStatus(): boolean {
if (!this.#hasNativeSupport()) {
return false;
}
return global[this.#globalName][this.#statusProperty];
}
subscribe(callback: (status: boolean) => void): () => void {
if (!this.#hasNativeSupport()) {
return () => {};
}
global[this.#globalName].subscribers.add(callback);
return () => {
global[this.#globalName].subscribers.delete(callback);
};
}
}
export default GlobalStateObserver;

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import NativeReactDevToolsSettingsManager from './specs/NativeReactDevToolsSettingsManager';
export function setGlobalHookSettings(settings: string) {
NativeReactDevToolsSettingsManager?.setGlobalHookSettings(settings);
}
export function getGlobalHookSettings(): ?string {
return NativeReactDevToolsSettingsManager?.getGlobalHookSettings();
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import Settings from '../../../../Libraries/Settings/Settings';
const GLOBAL_HOOK_SETTINGS = 'ReactDevTools::HookSettings';
export function setGlobalHookSettings(settings: string) {
Settings.set({
[GLOBAL_HOOK_SETTINGS]: settings,
});
}
export function getGlobalHookSettings(): ?string {
const value = Settings.get(GLOBAL_HOOK_SETTINGS);
if (typeof value === 'string') {
return value;
}
return null;
}

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
declare export function setGlobalHookSettings(settings: string): void;
declare export function getGlobalHookSettings(): ?string;

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import GlobalStateObserver from './GlobalStateObserver';
const observer = new GlobalStateObserver(
'__TRACING_STATE_OBSERVER__',
'isTracing',
);
const TracingStateObserver = {
isTracing(): boolean {
return observer.getStatus();
},
subscribe(callback: (isTracing: boolean) => void): () => void {
return observer.subscribe(callback);
},
};
export default TracingStateObserver;

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
type JSONValue =
| string
| number
| boolean
| null
| {[key: string]: JSONValue}
| Array<JSONValue>;
type DomainName = 'react-devtools';
class EventScope<T> {
#listeners: Set<(T) => void> = new Set();
addEventListener(listener: T => void): void {
this.#listeners.add(listener);
}
removeEventListener(listener: T => void): void {
this.#listeners.delete(listener);
}
emit(value: T): void {
// Assuming that listeners won't throw.
for (const listener of this.#listeners) {
listener(value);
}
}
}
export class Domain {
name: DomainName;
onMessage: EventScope<JSONValue>;
constructor(name: DomainName) {
if (global[FuseboxReactDevToolsDispatcher.BINDING_NAME] == null) {
throw new Error(
`Could not create domain ${name}: receiving end doesn't exist`,
);
}
this.name = name;
this.onMessage = new EventScope<JSONValue>();
}
sendMessage(message: JSONValue) {
const messageWithDomain = {domain: this.name, message};
const serializedMessageWithDomain = JSON.stringify(messageWithDomain);
global[FuseboxReactDevToolsDispatcher.BINDING_NAME](
serializedMessageWithDomain,
);
}
}
/**
* Globally bound object providing a hook for React DevTools runtime API calls
* over CDP.
*
* @see {@link ./__docs__/FuseboxReactDevToolsDispatcher.excalidraw.svg}
*/
class FuseboxReactDevToolsDispatcher {
static #domainNameToDomainMap: Map<DomainName, Domain> = new Map();
// Referenced and initialized from Chrome DevTools frontend.
static BINDING_NAME: string = '__CHROME_DEVTOOLS_FRONTEND_BINDING__';
static onDomainInitialization: EventScope<Domain> = new EventScope<Domain>();
// Should be private, referenced from Chrome DevTools frontend only.
static initializeDomain(domainName: DomainName): Domain {
const domain = new Domain(domainName);
this.#domainNameToDomainMap.set(domainName, domain);
this.onDomainInitialization.emit(domain);
return domain;
}
// Should be private, referenced from Chrome DevTools frontend only.
static sendMessage(domainName: DomainName, message: string): void {
const domain = this.#domainNameToDomainMap.get(domainName);
if (domain == null) {
throw new Error(
`Could not send message to ${domainName}: domain doesn't exist`,
);
}
try {
const parsedMessage = JSON.parse(message);
domain.onMessage.emit(parsedMessage);
} catch (err) {
console.error(
`Error while trying to send a message to domain ${domainName}:`,
err,
);
}
}
}
Object.defineProperty(global, '__FUSEBOX_REACT_DEVTOOLS_DISPATCHER__', {
value: FuseboxReactDevToolsDispatcher,
configurable: false,
enumerable: false,
writable: false,
});

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {TurboModule} from '../../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../../Libraries/TurboModule/TurboModuleRegistry';
export type ReloadAndProfileConfig = {
shouldReloadAndProfile: boolean,
recordChangeDescriptions: boolean,
};
// Linter doesn't speak Flow's `Partial` type
export type PartialReloadAndProfileConfig = {
shouldReloadAndProfile?: boolean,
recordChangeDescriptions?: boolean,
};
export interface Spec extends TurboModule {
+setReloadAndProfileConfig: (config: PartialReloadAndProfileConfig) => void;
+getReloadAndProfileConfig: () => ReloadAndProfileConfig;
}
export default (TurboModuleRegistry.get<Spec>(
'ReactDevToolsRuntimeSettingsModule',
): ?Spec);

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+setGlobalHookSettings: (settings: string) => void;
+getGlobalHookSettings: () => ?string;
}
export default (TurboModuleRegistry.get<Spec>(
'ReactDevToolsSettingsManager',
): ?Spec);

View File

@@ -0,0 +1,552 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<03688450419694f6d3f4fc709df4de9a>>
* @flow strict
* @noformat
*/
/**
* IMPORTANT: Do NOT modify this file directly.
*
* To change the definition of the flags, edit
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
*
* To regenerate this code, run the following script from the repo root:
* yarn featureflags --update
*/
import {
type Getter,
type OverridesFor,
createJavaScriptFlagGetter,
createNativeFlagGetter,
setOverrides,
} from './ReactNativeFeatureFlagsBase';
export type ReactNativeFeatureFlagsJsOnly = $ReadOnly<{
jsOnlyTestFlag: Getter<boolean>,
animatedShouldDebounceQueueFlush: Getter<boolean>,
animatedShouldUseSingleOp: Getter<boolean>,
deferFlatListFocusChangeRenderUpdate: Getter<boolean>,
disableMaintainVisibleContentPosition: Getter<boolean>,
enableOptimizedBoxShadowParsing: Getter<boolean>,
externalElementInspectionEnabled: Getter<boolean>,
fixImageSrcDimensionPropagation: Getter<boolean>,
fixVirtualizeListCollapseWindowSize: Getter<boolean>,
isLayoutAnimationEnabled: Getter<boolean>,
shouldUseAnimatedObjectForTransform: Getter<boolean>,
shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean>,
shouldUseSetNativePropsInFabric: Getter<boolean>,
}>;
export type ReactNativeFeatureFlagsJsOnlyOverrides = OverridesFor<ReactNativeFeatureFlagsJsOnly>;
export type ReactNativeFeatureFlags = $ReadOnly<{
...ReactNativeFeatureFlagsJsOnly,
commonTestFlag: Getter<boolean>,
commonTestFlagWithoutNativeImplementation: Getter<boolean>,
cdpInteractionMetricsEnabled: Getter<boolean>,
cxxNativeAnimatedEnabled: Getter<boolean>,
defaultTextToOverflowHidden: Getter<boolean>,
disableEarlyViewCommandExecution: Getter<boolean>,
disableImageViewPreallocationAndroid: Getter<boolean>,
disableMountItemReorderingAndroid: Getter<boolean>,
disableSubviewClippingAndroid: Getter<boolean>,
disableTextLayoutManagerCacheAndroid: Getter<boolean>,
disableViewPreallocationAndroid: Getter<boolean>,
enableAccessibilityOrder: Getter<boolean>,
enableAccumulatedUpdatesInRawPropsAndroid: Getter<boolean>,
enableAndroidAntialiasedBorderRadiusClipping: Getter<boolean>,
enableAndroidLinearText: Getter<boolean>,
enableAndroidTextMeasurementOptimizations: Getter<boolean>,
enableBridgelessArchitecture: Getter<boolean>,
enableCppPropsIteratorSetter: Getter<boolean>,
enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean>,
enableDestroyShadowTreeRevisionAsync: Getter<boolean>,
enableDoubleMeasurementFixAndroid: Getter<boolean>,
enableEagerMainQueueModulesOnIOS: Getter<boolean>,
enableEagerRootViewAttachment: Getter<boolean>,
enableExclusivePropsUpdateAndroid: Getter<boolean>,
enableFabricCommitBranching: Getter<boolean>,
enableFabricLogs: Getter<boolean>,
enableFabricRenderer: Getter<boolean>,
enableFontScaleChangesUpdatingLayout: Getter<boolean>,
enableIOSTextBaselineOffsetPerLine: Getter<boolean>,
enableIOSViewClipToPaddingBox: Getter<boolean>,
enableImagePrefetchingAndroid: Getter<boolean>,
enableImagePrefetchingJNIBatchingAndroid: Getter<boolean>,
enableImagePrefetchingOnUiThreadAndroid: Getter<boolean>,
enableImmediateUpdateModeForContentOffsetChanges: Getter<boolean>,
enableImperativeFocus: Getter<boolean>,
enableInteropViewManagerClassLookUpOptimizationIOS: Getter<boolean>,
enableIntersectionObserverByDefault: Getter<boolean>,
enableKeyEvents: Getter<boolean>,
enableLayoutAnimationsOnAndroid: Getter<boolean>,
enableLayoutAnimationsOnIOS: Getter<boolean>,
enableMainQueueCoordinatorOnIOS: Getter<boolean>,
enableModuleArgumentNSNullConversionIOS: Getter<boolean>,
enableNativeCSSParsing: Getter<boolean>,
enableNetworkEventReporting: Getter<boolean>,
enablePreparedTextLayout: Getter<boolean>,
enablePropsUpdateReconciliationAndroid: Getter<boolean>,
enableSwiftUIBasedFilters: Getter<boolean>,
enableViewCulling: Getter<boolean>,
enableViewRecycling: Getter<boolean>,
enableViewRecyclingForImage: Getter<boolean>,
enableViewRecyclingForScrollView: Getter<boolean>,
enableViewRecyclingForText: Getter<boolean>,
enableViewRecyclingForView: Getter<boolean>,
enableVirtualViewContainerStateExperimental: Getter<boolean>,
enableVirtualViewDebugFeatures: Getter<boolean>,
fixFindShadowNodeByTagRaceCondition: Getter<boolean>,
fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter<boolean>,
fixTextClippingAndroid15useBoundsForWidth: Getter<boolean>,
fuseboxAssertSingleHostState: Getter<boolean>,
fuseboxEnabledRelease: Getter<boolean>,
fuseboxNetworkInspectionEnabled: Getter<boolean>,
hideOffscreenVirtualViewsOnIOS: Getter<boolean>,
overrideBySynchronousMountPropsAtMountingAndroid: Getter<boolean>,
perfIssuesEnabled: Getter<boolean>,
perfMonitorV2Enabled: Getter<boolean>,
preparedTextCacheSize: Getter<number>,
preventShadowTreeCommitExhaustion: Getter<boolean>,
shouldPressibilityUseW3CPointerEventsForHover: Getter<boolean>,
shouldTriggerResponderTransferOnScrollAndroid: Getter<boolean>,
skipActivityIdentityAssertionOnHostPause: Getter<boolean>,
syncAndroidClipToPaddingWithOverflow: Getter<boolean>,
traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean>,
updateRuntimeShadowNodeReferencesOnCommit: Getter<boolean>,
updateRuntimeShadowNodeReferencesOnCommitThread: Getter<boolean>,
useAlwaysAvailableJSErrorHandling: Getter<boolean>,
useFabricInterop: Getter<boolean>,
useNativeViewConfigsInBridgelessMode: Getter<boolean>,
useNestedScrollViewAndroid: Getter<boolean>,
useSharedAnimatedBackend: Getter<boolean>,
useTraitHiddenOnAndroid: Getter<boolean>,
useTurboModuleInterop: Getter<boolean>,
useTurboModules: Getter<boolean>,
useUnorderedMapInDifferentiator: Getter<boolean>,
viewCullingOutsetRatio: Getter<number>,
viewTransitionEnabled: Getter<boolean>,
virtualViewPrerenderRatio: Getter<number>,
}>;
/**
* JS-only flag for testing. Do NOT modify.
*/
export const jsOnlyTestFlag: Getter<boolean> = createJavaScriptFlagGetter('jsOnlyTestFlag', false);
/**
* Enables an experimental flush-queue debouncing in Animated.js.
*/
export const animatedShouldDebounceQueueFlush: Getter<boolean> = createJavaScriptFlagGetter('animatedShouldDebounceQueueFlush', false);
/**
* Enables an experimental mega-operation for Animated.js that replaces many calls to native with a single call into native, to reduce JSI/JNI traffic.
*/
export const animatedShouldUseSingleOp: Getter<boolean> = createJavaScriptFlagGetter('animatedShouldUseSingleOp', false);
/**
* Use the deferred cell render update mechanism for focus change in FlatList.
*/
export const deferFlatListFocusChangeRenderUpdate: Getter<boolean> = createJavaScriptFlagGetter('deferFlatListFocusChangeRenderUpdate', false);
/**
* Disable prop maintainVisibleContentPosition in ScrollView
*/
export const disableMaintainVisibleContentPosition: Getter<boolean> = createJavaScriptFlagGetter('disableMaintainVisibleContentPosition', false);
/**
* Hoists regex patterns to module scope and optimizes parseLength in processBoxShadow for improved performance.
*/
export const enableOptimizedBoxShadowParsing: Getter<boolean> = createJavaScriptFlagGetter('enableOptimizedBoxShadowParsing', false);
/**
* Enable the external inspection API for DevTools to communicate with the Inspector overlay.
*/
export const externalElementInspectionEnabled: Getter<boolean> = createJavaScriptFlagGetter('externalElementInspectionEnabled', true);
/**
* Fix image dimensions not being passed through when src is used
*/
export const fixImageSrcDimensionPropagation: Getter<boolean> = createJavaScriptFlagGetter('fixImageSrcDimensionPropagation', true);
/**
* Fixing an edge case where the current window size is not properly calculated with fast scrolling. Window size collapsed to 1 element even if windowSize more than the current amount of elements
*/
export const fixVirtualizeListCollapseWindowSize: Getter<boolean> = createJavaScriptFlagGetter('fixVirtualizeListCollapseWindowSize', false);
/**
* Function used to enable / disabled Layout Animations in React Native.
*/
export const isLayoutAnimationEnabled: Getter<boolean> = createJavaScriptFlagGetter('isLayoutAnimationEnabled', true);
/**
* Enables use of AnimatedObject for animating transform values.
*/
export const shouldUseAnimatedObjectForTransform: Getter<boolean> = createJavaScriptFlagGetter('shouldUseAnimatedObjectForTransform', false);
/**
* removeClippedSubviews prop will be used as the default in FlatList on iOS to match Android
*/
export const shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter<boolean> = createJavaScriptFlagGetter('shouldUseRemoveClippedSubviewsAsDefaultOnIOS', false);
/**
* Enables use of setNativeProps in JS driven animations.
*/
export const shouldUseSetNativePropsInFabric: Getter<boolean> = createJavaScriptFlagGetter('shouldUseSetNativePropsInFabric', true);
/**
* Common flag for testing. Do NOT modify.
*/
export const commonTestFlag: Getter<boolean> = createNativeFlagGetter('commonTestFlag', false);
/**
* Common flag for testing (without native implementation). Do NOT modify.
*/
export const commonTestFlagWithoutNativeImplementation: Getter<boolean> = createNativeFlagGetter('commonTestFlagWithoutNativeImplementation', false);
/**
* Enable emitting of InteractionEntry live metrics to the debugger. Requires `enableBridgelessArchitecture`.
*/
export const cdpInteractionMetricsEnabled: Getter<boolean> = createNativeFlagGetter('cdpInteractionMetricsEnabled', false);
/**
* Use a C++ implementation of Native Animated instead of the platform implementation.
*/
export const cxxNativeAnimatedEnabled: Getter<boolean> = createNativeFlagGetter('cxxNativeAnimatedEnabled', false);
/**
* When enabled, sets the default overflow style for Text components to hidden instead of visible.
*/
export const defaultTextToOverflowHidden: Getter<boolean> = createNativeFlagGetter('defaultTextToOverflowHidden', true);
/**
* Dispatch view commands in mount item order.
*/
export const disableEarlyViewCommandExecution: Getter<boolean> = createNativeFlagGetter('disableEarlyViewCommandExecution', false);
/**
* Force disable view preallocation for images triggered from createNode off the main thread on Android
*/
export const disableImageViewPreallocationAndroid: Getter<boolean> = createNativeFlagGetter('disableImageViewPreallocationAndroid', false);
/**
* Prevent FabricMountingManager from reordering mountItems, which may lead to invalid state on the UI thread
*/
export const disableMountItemReorderingAndroid: Getter<boolean> = createNativeFlagGetter('disableMountItemReorderingAndroid', false);
/**
* Force disable subview clipping for ReactViewGroup on Android
*/
export const disableSubviewClippingAndroid: Getter<boolean> = createNativeFlagGetter('disableSubviewClippingAndroid', false);
/**
* Turns off the global measurement cache used by TextLayoutManager on Android.
*/
export const disableTextLayoutManagerCacheAndroid: Getter<boolean> = createNativeFlagGetter('disableTextLayoutManagerCacheAndroid', false);
/**
* Force disable view preallocation triggered from createNode off the main thread on Android
*/
export const disableViewPreallocationAndroid: Getter<boolean> = createNativeFlagGetter('disableViewPreallocationAndroid', false);
/**
* When enabled, the accessibilityOrder prop will propagate to native platforms and define the accessibility order.
*/
export const enableAccessibilityOrder: Getter<boolean> = createNativeFlagGetter('enableAccessibilityOrder', false);
/**
* When enabled, Android will accumulate updates in rawProps to reduce the number of mounting instructions for cascading re-renders.
*/
export const enableAccumulatedUpdatesInRawPropsAndroid: Getter<boolean> = createNativeFlagGetter('enableAccumulatedUpdatesInRawPropsAndroid', false);
/**
* Enable antialiased border radius clipping for Android API 28 and below using manual masking with Porter-Duff compositing
*/
export const enableAndroidAntialiasedBorderRadiusClipping: Getter<boolean> = createNativeFlagGetter('enableAndroidAntialiasedBorderRadiusClipping', false);
/**
* Enables linear text rendering on Android wherever subpixel text rendering is enabled
*/
export const enableAndroidLinearText: Getter<boolean> = createNativeFlagGetter('enableAndroidLinearText', false);
/**
* Enables various optimizations throughout the path of measuring text on Android.
*/
export const enableAndroidTextMeasurementOptimizations: Getter<boolean> = createNativeFlagGetter('enableAndroidTextMeasurementOptimizations', false);
/**
* Feature flag to enable the new bridgeless architecture. Note: Enabling this will force enable the following flags: `useTurboModules` & `enableFabricRenderer`.
*/
export const enableBridgelessArchitecture: Getter<boolean> = createNativeFlagGetter('enableBridgelessArchitecture', false);
/**
* Enable prop iterator setter-style construction of Props in C++ (this flag is not used in Java).
*/
export const enableCppPropsIteratorSetter: Getter<boolean> = createNativeFlagGetter('enableCppPropsIteratorSetter', false);
/**
* This enables the fabric implementation of focus search so that we can focus clipped elements
*/
export const enableCustomFocusSearchOnClippedElementsAndroid: Getter<boolean> = createNativeFlagGetter('enableCustomFocusSearchOnClippedElementsAndroid', true);
/**
* Enables destructor calls for ShadowTreeRevision in the background to reduce UI thread work.
*/
export const enableDestroyShadowTreeRevisionAsync: Getter<boolean> = createNativeFlagGetter('enableDestroyShadowTreeRevisionAsync', false);
/**
* When enabled a subset of components will avoid double measurement on Android.
*/
export const enableDoubleMeasurementFixAndroid: Getter<boolean> = createNativeFlagGetter('enableDoubleMeasurementFixAndroid', false);
/**
* This infra allows native modules to initialize on the main thread, during React Native init.
*/
export const enableEagerMainQueueModulesOnIOS: Getter<boolean> = createNativeFlagGetter('enableEagerMainQueueModulesOnIOS', false);
/**
* Feature flag to configure eager attachment of the root view/initialisation of the JS code.
*/
export const enableEagerRootViewAttachment: Getter<boolean> = createNativeFlagGetter('enableEagerRootViewAttachment', false);
/**
* When enabled, Android will disable Props 1.5 raw value merging when Props 2.0 is available.
*/
export const enableExclusivePropsUpdateAndroid: Getter<boolean> = createNativeFlagGetter('enableExclusivePropsUpdateAndroid', false);
/**
* Enables Fabric commit branching to fix starvation problems and atomic JS updates.
*/
export const enableFabricCommitBranching: Getter<boolean> = createNativeFlagGetter('enableFabricCommitBranching', false);
/**
* This feature flag enables logs for Fabric.
*/
export const enableFabricLogs: Getter<boolean> = createNativeFlagGetter('enableFabricLogs', false);
/**
* Enables the use of the Fabric renderer in the whole app.
*/
export const enableFabricRenderer: Getter<boolean> = createNativeFlagGetter('enableFabricRenderer', false);
/**
* Enables font scale changes updating layout for measurable nodes.
*/
export const enableFontScaleChangesUpdatingLayout: Getter<boolean> = createNativeFlagGetter('enableFontScaleChangesUpdatingLayout', true);
/**
* Applies base offset for each line of text separately on iOS.
*/
export const enableIOSTextBaselineOffsetPerLine: Getter<boolean> = createNativeFlagGetter('enableIOSTextBaselineOffsetPerLine', false);
/**
* iOS Views will clip to their padding box vs border box
*/
export const enableIOSViewClipToPaddingBox: Getter<boolean> = createNativeFlagGetter('enableIOSViewClipToPaddingBox', false);
/**
* When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout
*/
export const enableImagePrefetchingAndroid: Getter<boolean> = createNativeFlagGetter('enableImagePrefetchingAndroid', false);
/**
* When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call
*/
export const enableImagePrefetchingJNIBatchingAndroid: Getter<boolean> = createNativeFlagGetter('enableImagePrefetchingJNIBatchingAndroid', false);
/**
* When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread
*/
export const enableImagePrefetchingOnUiThreadAndroid: Getter<boolean> = createNativeFlagGetter('enableImagePrefetchingOnUiThreadAndroid', false);
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
export const enableImmediateUpdateModeForContentOffsetChanges: Getter<boolean> = createNativeFlagGetter('enableImmediateUpdateModeForContentOffsetChanges', false);
/**
* Enable ref.focus() and ref.blur() for all views, not just TextInput.
*/
export const enableImperativeFocus: Getter<boolean> = createNativeFlagGetter('enableImperativeFocus', false);
/**
* This is to fix the issue with interop view manager where component descriptor lookup is causing ViewManager to preload.
*/
export const enableInteropViewManagerClassLookUpOptimizationIOS: Getter<boolean> = createNativeFlagGetter('enableInteropViewManagerClassLookUpOptimizationIOS', false);
/**
* Enables the IntersectionObserver Web API in React Native.
*/
export const enableIntersectionObserverByDefault: Getter<boolean> = createNativeFlagGetter('enableIntersectionObserverByDefault', false);
/**
* Enables key up/down/press events to be sent to JS from components
*/
export const enableKeyEvents: Getter<boolean> = createNativeFlagGetter('enableKeyEvents', false);
/**
* When enabled, LayoutAnimations API will animate state changes on Android.
*/
export const enableLayoutAnimationsOnAndroid: Getter<boolean> = createNativeFlagGetter('enableLayoutAnimationsOnAndroid', false);
/**
* When enabled, LayoutAnimations API will animate state changes on iOS.
*/
export const enableLayoutAnimationsOnIOS: Getter<boolean> = createNativeFlagGetter('enableLayoutAnimationsOnIOS', true);
/**
* Make RCTUnsafeExecuteOnMainQueueSync less likely to deadlock, when used in conjuction with sync rendering/events.
*/
export const enableMainQueueCoordinatorOnIOS: Getter<boolean> = createNativeFlagGetter('enableMainQueueCoordinatorOnIOS', false);
/**
* Enable NSNull conversion when handling module arguments on iOS
*/
export const enableModuleArgumentNSNullConversionIOS: Getter<boolean> = createNativeFlagGetter('enableModuleArgumentNSNullConversionIOS', false);
/**
* Parse CSS strings using the Fabric CSS parser instead of ViewConfig processing
*/
export const enableNativeCSSParsing: Getter<boolean> = createNativeFlagGetter('enableNativeCSSParsing', false);
/**
* Enable network event reporting hooks in each native platform through `NetworkReporter` (Web Perf APIs + CDP). This flag should be combined with `fuseboxNetworkInspectionEnabled` to enable Network CDP debugging.
*/
export const enableNetworkEventReporting: Getter<boolean> = createNativeFlagGetter('enableNetworkEventReporting', true);
/**
* Enables caching text layout artifacts for later reuse
*/
export const enablePreparedTextLayout: Getter<boolean> = createNativeFlagGetter('enablePreparedTextLayout', false);
/**
* When enabled, Android will receive prop updates based on the differences between the last rendered shadow node and the last committed shadow node.
*/
export const enablePropsUpdateReconciliationAndroid: Getter<boolean> = createNativeFlagGetter('enablePropsUpdateReconciliationAndroid', false);
/**
* When enabled, it will use SwiftUI for filter effects like blur on iOS.
*/
export const enableSwiftUIBasedFilters: Getter<boolean> = createNativeFlagGetter('enableSwiftUIBasedFilters', false);
/**
* Enables View Culling: as soon as a view goes off screen, it can be reused anywhere in the UI and pieced together with other items to create new UI elements.
*/
export const enableViewCulling: Getter<boolean> = createNativeFlagGetter('enableViewCulling', false);
/**
* Enables View Recycling. When enabled, individual ViewManagers must still opt-in.
*/
export const enableViewRecycling: Getter<boolean> = createNativeFlagGetter('enableViewRecycling', false);
/**
* Enables View Recycling for <Image> via ReactViewGroup/ReactViewManager.
*/
export const enableViewRecyclingForImage: Getter<boolean> = createNativeFlagGetter('enableViewRecyclingForImage', true);
/**
* Enables View Recycling for <ScrollView> via ReactViewGroup/ReactViewManager.
*/
export const enableViewRecyclingForScrollView: Getter<boolean> = createNativeFlagGetter('enableViewRecyclingForScrollView', false);
/**
* Enables View Recycling for <Text> via ReactTextView/ReactTextViewManager.
*/
export const enableViewRecyclingForText: Getter<boolean> = createNativeFlagGetter('enableViewRecyclingForText', true);
/**
* Enables View Recycling for <View> via ReactViewGroup/ReactViewManager.
*/
export const enableViewRecyclingForView: Getter<boolean> = createNativeFlagGetter('enableViewRecyclingForView', true);
/**
* Enables the experimental version of `VirtualViewContainerState`.
*/
export const enableVirtualViewContainerStateExperimental: Getter<boolean> = createNativeFlagGetter('enableVirtualViewContainerStateExperimental', false);
/**
* Enables VirtualView debug features such as logging and overlays.
*/
export const enableVirtualViewDebugFeatures: Getter<boolean> = createNativeFlagGetter('enableVirtualViewDebugFeatures', false);
/**
* Fix a use-after-free race condition in findShadowNodeByTag_DEPRECATED by using getCurrentRevision() instead of tryCommit() with a raw pointer.
*/
export const fixFindShadowNodeByTagRaceCondition: Getter<boolean> = createNativeFlagGetter('fixFindShadowNodeByTagRaceCondition', false);
/**
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
*/
export const fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter<boolean> = createNativeFlagGetter('fixMappingOfEventPrioritiesBetweenFabricAndReact', false);
/**
* Fix text clipping starting in Android 15 due to usage of useBoundsForWidth
*/
export const fixTextClippingAndroid15useBoundsForWidth: Getter<boolean> = createNativeFlagGetter('fixTextClippingAndroid15useBoundsForWidth', false);
/**
* Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`.
*/
export const fuseboxAssertSingleHostState: Getter<boolean> = createNativeFlagGetter('fuseboxAssertSingleHostState', true);
/**
* Flag determining if the React Native DevTools (Fusebox) CDP backend should be enabled in release builds. This flag is global and should not be changed across React Host lifetimes.
*/
export const fuseboxEnabledRelease: Getter<boolean> = createNativeFlagGetter('fuseboxEnabledRelease', false);
/**
* Enable network inspection support in the React Native DevTools CDP backend. Requires `enableBridgelessArchitecture`. This flag is global and should not be changed across React Host lifetimes.
*/
export const fuseboxNetworkInspectionEnabled: Getter<boolean> = createNativeFlagGetter('fuseboxNetworkInspectionEnabled', true);
/**
* Hides offscreen VirtualViews on iOS by setting hidden = YES to avoid extra cost of views
*/
export const hideOffscreenVirtualViewsOnIOS: Getter<boolean> = createNativeFlagGetter('hideOffscreenVirtualViewsOnIOS', false);
/**
* Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated.
*/
export const overrideBySynchronousMountPropsAtMountingAndroid: Getter<boolean> = createNativeFlagGetter('overrideBySynchronousMountPropsAtMountingAndroid', false);
/**
* Enable reporting Performance Issues (`detail.devtools.performanceIssue`). Displayed in the V2 Performance Monitor and the "Performance Issues" sub-panel in DevTools.
*/
export const perfIssuesEnabled: Getter<boolean> = createNativeFlagGetter('perfIssuesEnabled', false);
/**
* Enable the V2 in-app Performance Monitor. This flag is global and should not be changed across React Host lifetimes.
*/
export const perfMonitorV2Enabled: Getter<boolean> = createNativeFlagGetter('perfMonitorV2Enabled', false);
/**
* Number cached PreparedLayouts in TextLayoutManager cache
*/
export const preparedTextCacheSize: Getter<number> = createNativeFlagGetter('preparedTextCacheSize', 200);
/**
* Enables a new mechanism in ShadowTree to prevent problems caused by multiple threads trying to commit concurrently. If a thread tries to commit a few times unsuccessfully, it will acquire a lock and try again.
*/
export const preventShadowTreeCommitExhaustion: Getter<boolean> = createNativeFlagGetter('preventShadowTreeCommitExhaustion', false);
/**
* Function used to enable / disable Pressibility from using W3C Pointer Events for its hover callbacks
*/
export const shouldPressibilityUseW3CPointerEventsForHover: Getter<boolean> = createNativeFlagGetter('shouldPressibilityUseW3CPointerEventsForHover', false);
/**
* Do not emit touchcancel from Android ScrollView, instead native topScroll event will trigger responder transfer and terminate in RN renderer.
*/
export const shouldTriggerResponderTransferOnScrollAndroid: Getter<boolean> = createNativeFlagGetter('shouldTriggerResponderTransferOnScrollAndroid', false);
/**
* Skip activity identity assertion in ReactHostImpl::onHostPause()
*/
export const skipActivityIdentityAssertionOnHostPause: Getter<boolean> = createNativeFlagGetter('skipActivityIdentityAssertionOnHostPause', false);
/**
* Sync clipToPadding on Android views with the overflow property
*/
export const syncAndroidClipToPaddingWithOverflow: Getter<boolean> = createNativeFlagGetter('syncAndroidClipToPaddingWithOverflow', false);
/**
* Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause.
*/
export const traceTurboModulePromiseRejectionsOnAndroid: Getter<boolean> = createNativeFlagGetter('traceTurboModulePromiseRejectionsOnAndroid', false);
/**
* When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.
*/
export const updateRuntimeShadowNodeReferencesOnCommit: Getter<boolean> = createNativeFlagGetter('updateRuntimeShadowNodeReferencesOnCommit', false);
/**
* When enabled, runtime shadow node references will be updated during the commit only on the allowed thread.
*/
export const updateRuntimeShadowNodeReferencesOnCommitThread: Getter<boolean> = createNativeFlagGetter('updateRuntimeShadowNodeReferencesOnCommitThread', false);
/**
* In Bridgeless mode, use the always available javascript error reporting pipeline.
*/
export const useAlwaysAvailableJSErrorHandling: Getter<boolean> = createNativeFlagGetter('useAlwaysAvailableJSErrorHandling', false);
/**
* Should this application enable the Fabric Interop Layer for Android? If yes, the application will behave so that it can accept non-Fabric components and render them on Fabric. This toggle is controlling extra logic such as custom event dispatching that are needed for the Fabric Interop Layer to work correctly.
*/
export const useFabricInterop: Getter<boolean> = createNativeFlagGetter('useFabricInterop', true);
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
export const useNativeViewConfigsInBridgelessMode: Getter<boolean> = createNativeFlagGetter('useNativeViewConfigsInBridgelessMode', false);
/**
* When enabled, ReactScrollView will extend NestedScrollView instead of ScrollView on Android for improved nested scrolling support.
*/
export const useNestedScrollViewAndroid: Getter<boolean> = createNativeFlagGetter('useNestedScrollViewAndroid', false);
/**
* Use shared animation backend in C++ Animated
*/
export const useSharedAnimatedBackend: Getter<boolean> = createNativeFlagGetter('useSharedAnimatedBackend', false);
/**
* Use Trait::hidden on Android
*/
export const useTraitHiddenOnAndroid: Getter<boolean> = createNativeFlagGetter('useTraitHiddenOnAndroid', false);
/**
* In Bridgeless mode, should legacy NativeModules use the TurboModule system?
*/
export const useTurboModuleInterop: Getter<boolean> = createNativeFlagGetter('useTurboModuleInterop', false);
/**
* When enabled, NativeModules will be executed by using the TurboModule system
*/
export const useTurboModules: Getter<boolean> = createNativeFlagGetter('useTurboModules', false);
/**
* Use std::unordered_map instead of TinyMap in the Differentiator for improved lookup performance.
*/
export const useUnorderedMapInDifferentiator: Getter<boolean> = createNativeFlagGetter('useUnorderedMapInDifferentiator', false);
/**
* Outset the culling context frame with the provided ratio. The culling context frame size will be outset by width * ratio on the left and right, and height * ratio on the top and bottom.
*/
export const viewCullingOutsetRatio: Getter<number> = createNativeFlagGetter('viewCullingOutsetRatio', 0);
/**
* Enable the View Transition API for animating transitions between views.
*/
export const viewTransitionEnabled: Getter<boolean> = createNativeFlagGetter('viewTransitionEnabled', false);
/**
* Initial prerender ratio for VirtualView.
*/
export const virtualViewPrerenderRatio: Getter<number> = createNativeFlagGetter('virtualViewPrerenderRatio', 5);
/**
* Overrides the feature flags with the provided methods.
* NOTE: Only JS-only flags can be overridden from JavaScript using this API.
*/
export const override = setOverrides;

View File

@@ -0,0 +1,136 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {
ReactNativeFeatureFlagsJsOnly,
ReactNativeFeatureFlagsJsOnlyOverrides,
} from './ReactNativeFeatureFlags';
import NativeReactNativeFeatureFlags from './specs/NativeReactNativeFeatureFlags';
const accessedFeatureFlags: Set<string> = new Set();
let overrides: ?ReactNativeFeatureFlagsJsOnlyOverrides;
// This is a list of functions to clear the cached value for each feature flag
// getter. This is only used in development.
const clearCachedValuesFns: Array<() => void> = [];
export type Getter<T> = () => T;
// This defines the types for the overrides object, whose methods can return
// null or undefined to fallback to the default value.
export type OverridesFor<T> = Partial<{
[key in keyof T]: Getter<?ReturnType<T[key]>>,
}>;
function createGetter<T: boolean | number | string>(
configName: string,
customValueGetter: Getter<?T>,
defaultValue: T,
): Getter<T> {
let cachedValue: ?T;
if (__DEV__) {
clearCachedValuesFns.push(() => {
cachedValue = undefined;
});
}
return () => {
if (cachedValue == null) {
cachedValue = customValueGetter() ?? defaultValue;
}
return cachedValue;
};
}
export function createJavaScriptFlagGetter<
K: keyof ReactNativeFeatureFlagsJsOnly,
>(
configName: K,
defaultValue: ReturnType<ReactNativeFeatureFlagsJsOnly[K]>,
): Getter<ReturnType<ReactNativeFeatureFlagsJsOnly[K]>> {
return createGetter(
configName,
() => {
accessedFeatureFlags.add(configName);
return overrides?.[configName]?.();
},
defaultValue,
);
}
type NativeFeatureFlags = NonNullable<typeof NativeReactNativeFeatureFlags>;
export function createNativeFlagGetter<K: keyof NativeFeatureFlags>(
configName: K,
defaultValue: ReturnType<NonNullable<NativeFeatureFlags[K]>>,
skipUnavailableNativeModuleError: boolean = false,
): Getter<ReturnType<NonNullable<NativeFeatureFlags[K]>>> {
return createGetter(
configName,
() => {
maybeLogUnavailableNativeModuleError(configName);
return NativeReactNativeFeatureFlags?.[configName]?.();
},
defaultValue,
);
}
export function getOverrides(): ?ReactNativeFeatureFlagsJsOnlyOverrides {
return overrides;
}
export function setOverrides(
newOverrides: ReactNativeFeatureFlagsJsOnlyOverrides,
): void {
if (overrides != null) {
throw new Error('Feature flags cannot be overridden more than once');
}
if (accessedFeatureFlags.size > 0) {
const accessedFeatureFlagsStr = Array.from(accessedFeatureFlags).join(', ');
throw new Error(
`Feature flags were accessed before being overridden: ${accessedFeatureFlagsStr}`,
);
}
overrides = newOverrides;
}
const reportedConfigNames: Set<string> = new Set();
const hasTurboModules =
global.RN$Bridgeless === true || global.__turboModuleProxy != null;
function maybeLogUnavailableNativeModuleError(configName: string): void {
if (
!NativeReactNativeFeatureFlags &&
// Don't log in tests.
process.env.NODE_ENV !== 'test' &&
// Don't log more than once per config
!reportedConfigNames.has(configName) &&
// Don't log in the legacy architecture.
hasTurboModules
) {
reportedConfigNames.add(configName);
console.error(
`Could not access feature flag '${configName}' because native module method was not available`,
);
}
}
export function dangerouslyResetForTesting(): void {
if (__DEV__) {
overrides = null;
accessedFeatureFlags.clear();
reportedConfigNames.clear();
clearCachedValuesFns.forEach(fn => fn());
}
}

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2955ab3f744af8b5cdf587312ba423d7>>
* @flow strict
* @noformat
*/
/**
* IMPORTANT: Do NOT modify this file directly.
*
* To change the definition of the flags, edit
* packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js.
*
* To regenerate this code, run the following script from the repo root:
* yarn featureflags --update
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+commonTestFlag?: () => boolean;
+commonTestFlagWithoutNativeImplementation?: () => boolean;
+cdpInteractionMetricsEnabled?: () => boolean;
+cxxNativeAnimatedEnabled?: () => boolean;
+defaultTextToOverflowHidden?: () => boolean;
+disableEarlyViewCommandExecution?: () => boolean;
+disableImageViewPreallocationAndroid?: () => boolean;
+disableMountItemReorderingAndroid?: () => boolean;
+disableSubviewClippingAndroid?: () => boolean;
+disableTextLayoutManagerCacheAndroid?: () => boolean;
+disableViewPreallocationAndroid?: () => boolean;
+enableAccessibilityOrder?: () => boolean;
+enableAccumulatedUpdatesInRawPropsAndroid?: () => boolean;
+enableAndroidAntialiasedBorderRadiusClipping?: () => boolean;
+enableAndroidLinearText?: () => boolean;
+enableAndroidTextMeasurementOptimizations?: () => boolean;
+enableBridgelessArchitecture?: () => boolean;
+enableCppPropsIteratorSetter?: () => boolean;
+enableCustomFocusSearchOnClippedElementsAndroid?: () => boolean;
+enableDestroyShadowTreeRevisionAsync?: () => boolean;
+enableDoubleMeasurementFixAndroid?: () => boolean;
+enableEagerMainQueueModulesOnIOS?: () => boolean;
+enableEagerRootViewAttachment?: () => boolean;
+enableExclusivePropsUpdateAndroid?: () => boolean;
+enableFabricCommitBranching?: () => boolean;
+enableFabricLogs?: () => boolean;
+enableFabricRenderer?: () => boolean;
+enableFontScaleChangesUpdatingLayout?: () => boolean;
+enableIOSTextBaselineOffsetPerLine?: () => boolean;
+enableIOSViewClipToPaddingBox?: () => boolean;
+enableImagePrefetchingAndroid?: () => boolean;
+enableImagePrefetchingJNIBatchingAndroid?: () => boolean;
+enableImagePrefetchingOnUiThreadAndroid?: () => boolean;
+enableImmediateUpdateModeForContentOffsetChanges?: () => boolean;
+enableImperativeFocus?: () => boolean;
+enableInteropViewManagerClassLookUpOptimizationIOS?: () => boolean;
+enableIntersectionObserverByDefault?: () => boolean;
+enableKeyEvents?: () => boolean;
+enableLayoutAnimationsOnAndroid?: () => boolean;
+enableLayoutAnimationsOnIOS?: () => boolean;
+enableMainQueueCoordinatorOnIOS?: () => boolean;
+enableModuleArgumentNSNullConversionIOS?: () => boolean;
+enableNativeCSSParsing?: () => boolean;
+enableNetworkEventReporting?: () => boolean;
+enablePreparedTextLayout?: () => boolean;
+enablePropsUpdateReconciliationAndroid?: () => boolean;
+enableSwiftUIBasedFilters?: () => boolean;
+enableViewCulling?: () => boolean;
+enableViewRecycling?: () => boolean;
+enableViewRecyclingForImage?: () => boolean;
+enableViewRecyclingForScrollView?: () => boolean;
+enableViewRecyclingForText?: () => boolean;
+enableViewRecyclingForView?: () => boolean;
+enableVirtualViewContainerStateExperimental?: () => boolean;
+enableVirtualViewDebugFeatures?: () => boolean;
+fixFindShadowNodeByTagRaceCondition?: () => boolean;
+fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean;
+fixTextClippingAndroid15useBoundsForWidth?: () => boolean;
+fuseboxAssertSingleHostState?: () => boolean;
+fuseboxEnabledRelease?: () => boolean;
+fuseboxNetworkInspectionEnabled?: () => boolean;
+hideOffscreenVirtualViewsOnIOS?: () => boolean;
+overrideBySynchronousMountPropsAtMountingAndroid?: () => boolean;
+perfIssuesEnabled?: () => boolean;
+perfMonitorV2Enabled?: () => boolean;
+preparedTextCacheSize?: () => number;
+preventShadowTreeCommitExhaustion?: () => boolean;
+shouldPressibilityUseW3CPointerEventsForHover?: () => boolean;
+shouldTriggerResponderTransferOnScrollAndroid?: () => boolean;
+skipActivityIdentityAssertionOnHostPause?: () => boolean;
+syncAndroidClipToPaddingWithOverflow?: () => boolean;
+traceTurboModulePromiseRejectionsOnAndroid?: () => boolean;
+updateRuntimeShadowNodeReferencesOnCommit?: () => boolean;
+updateRuntimeShadowNodeReferencesOnCommitThread?: () => boolean;
+useAlwaysAvailableJSErrorHandling?: () => boolean;
+useFabricInterop?: () => boolean;
+useNativeViewConfigsInBridgelessMode?: () => boolean;
+useNestedScrollViewAndroid?: () => boolean;
+useSharedAnimatedBackend?: () => boolean;
+useTraitHiddenOnAndroid?: () => boolean;
+useTurboModuleInterop?: () => boolean;
+useTurboModules?: () => boolean;
+useUnorderedMapInDifferentiator?: () => boolean;
+viewCullingOutsetRatio?: () => number;
+viewTransitionEnabled?: () => boolean;
+virtualViewPrerenderRatio?: () => number;
}
const NativeReactNativeFeatureFlags: ?Spec = TurboModuleRegistry.get<Spec>(
'NativeReactNativeFeatureFlagsCxx',
);
export default NativeReactNativeFeatureFlags;

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {ExtendedError} from '../../../../Libraries/Core/ExtendedError';
import ExceptionsManager, {
SyntheticError,
} from '../../../../Libraries/Core/ExceptionsManager';
import * as React from 'react';
type ErrorInfo = {
+componentStack?: ?string,
// $FlowFixMe[unclear-type] unknown props and state.
+errorBoundary?: ?React.Component<any, any>,
};
function getExtendedError(
errorValue: unknown,
errorInfo: ErrorInfo,
): ExtendedError {
let error;
// Typically, `errorValue` should be an error. However, other values such as
// strings (or even null) are sometimes thrown.
if (errorValue instanceof Error) {
/* $FlowFixMe[class-object-subtyping] added when improving typing for
* this parameters */
// $FlowFixMe[incompatible-type]
error = (errorValue: ExtendedError);
} else if (typeof errorValue === 'string') {
/* $FlowFixMe[class-object-subtyping] added when improving typing for
* this parameters */
// $FlowFixMe[incompatible-type]
error = (new SyntheticError(errorValue): ExtendedError);
} else {
/* $FlowFixMe[class-object-subtyping] added when improving typing for
* this parameters */
// $FlowFixMe[incompatible-type]
error = (new SyntheticError('Unspecified error'): ExtendedError);
}
try {
// $FlowFixMe[incompatible-use] this is in try/catch.
error.componentStack = errorInfo.componentStack;
error.isComponentError = true;
} catch {
// Ignored.
}
return error;
}
export function onUncaughtError(
errorValue: unknown,
errorInfo: ErrorInfo,
): void {
const error = getExtendedError(errorValue, errorInfo);
// Uncaught errors are fatal.
ExceptionsManager.handleException(error, true);
}
export function onCaughtError(errorValue: unknown, errorInfo: ErrorInfo): void {
const error = getExtendedError(errorValue, errorInfo);
// Caught errors are not fatal.
ExceptionsManager.handleException(error, false);
}
export function onRecoverableError(
errorValue: unknown,
errorInfo: ErrorInfo,
): void {
const error = getExtendedError(errorValue, errorInfo);
// Recoverable errors should only be warnings.
// This will make it a soft error in LogBox.
// TODO: improve the logging for recoverable errors in prod.
console.warn(error);
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
let initialized = false;
export default function setUpDOM() {
if (initialized) {
return;
}
initialized = true;
polyfillGlobal(
'DOMRect',
() => require('../webapis/geometry/DOMRect').default,
);
polyfillGlobal(
'DOMRectReadOnly',
() => require('../webapis/geometry/DOMRectReadOnly').default,
);
polyfillGlobal(
'DOMRectList',
() => require('../webapis/geometry/DOMRectList').default,
);
polyfillGlobal(
'HTMLCollection',
() => require('../webapis/dom/oldstylecollections/HTMLCollection').default,
);
polyfillGlobal(
'NodeList',
() => require('../webapis/dom/oldstylecollections/NodeList').default,
);
polyfillGlobal(
'Node',
() => require('../webapis/dom/nodes/ReadOnlyNode').default,
);
polyfillGlobal(
'Document',
() => require('../webapis/dom/nodes/ReactNativeDocument').default,
);
polyfillGlobal(
'CharacterData',
() => require('../webapis/dom/nodes/ReadOnlyCharacterData').default,
);
polyfillGlobal(
'Text',
() => require('../webapis/dom/nodes/ReadOnlyText').default,
);
polyfillGlobal(
'Element',
() => require('../webapis/dom/nodes/ReadOnlyElement').default,
);
polyfillGlobal(
'HTMLElement',
() => require('../webapis/dom/nodes/ReactNativeElement').default,
);
}

View File

@@ -0,0 +1,50 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
let initialized = false;
export default function setUpDefaltReactNativeEnvironment(
enableDeveloperTools: boolean = true,
) {
if (initialized) {
return;
}
initialized = true;
require('../../../Libraries/Core/setUpGlobals');
require('./setUpDOM').default();
require('../../../Libraries/Core/setUpPerformance');
require('../../../Libraries/Core/polyfillPromise');
require('../../../Libraries/Core/setUpTimers');
if (__DEV__ && enableDeveloperTools) {
require('../../../Libraries/Core/setUpReactDevTools');
}
require('../../../Libraries/Core/setUpErrorHandling');
require('../../../Libraries/Core/setUpRegeneratorRuntime');
require('../../../Libraries/Core/setUpXHR');
require('../../../Libraries/Core/setUpAlert');
require('../../../Libraries/Core/setUpNavigator');
require('../../../Libraries/Core/setUpBatchedBridge');
require('../../../Libraries/Core/setUpSegmentFetcher');
if (__DEV__ && enableDeveloperTools) {
require('../../../Libraries/Core/checkNativeVersion');
require('../../../Libraries/Core/setUpDeveloperTools');
require('../../../Libraries/LogBox/LogBox').default.install();
}
require('../../../Libraries/ReactNative/AppRegistry');
if (
require('../../../src/private/featureflags/ReactNativeFeatureFlags').enableIntersectionObserverByDefault()
) {
require('./setUpIntersectionObserver').default();
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
let initialized = false;
export default function setUpIntersectionObserver() {
if (initialized) {
return;
}
initialized = true;
polyfillGlobal(
'IntersectionObserver',
() =>
require('../webapis/intersectionobserver/IntersectionObserver').default,
);
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
let initialized = false;
export default function setUpMutationObserver() {
if (initialized) {
return;
}
initialized = true;
polyfillGlobal(
'MutationObserver',
() => require('../webapis/mutationobserver/MutationObserver').default,
);
polyfillGlobal(
'MutationRecord',
() => require('../webapis/mutationobserver/MutationRecord').default,
);
}

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import {polyfillGlobal} from '../../../Libraries/Utilities/PolyfillFunctions';
let initialized = false;
export default function setUpPerformanceModern() {
if (initialized) {
return;
}
initialized = true;
const Performance = require('../webapis/performance/Performance').default;
// We don't use `polyfillGlobal` to define this lazily because the
// `performance` object is always accessed.
// $FlowExpectedError[cannot-write]
global.performance = new Performance();
polyfillGlobal(
'EventCounts',
() => require('../webapis/performance/EventTiming').EventCounts_public,
);
polyfillGlobal(
'Performance',
() => require('../webapis/performance/Performance').Performance_public,
);
polyfillGlobal(
'PerformanceEntry',
() =>
require('../webapis/performance/PerformanceEntry')
.PerformanceEntry_public,
);
polyfillGlobal(
'PerformanceEventTiming',
() =>
require('../webapis/performance/EventTiming')
.PerformanceEventTiming_public,
);
polyfillGlobal(
'PerformanceLongTaskTiming',
() =>
require('../webapis/performance/LongTasks')
.PerformanceLongTaskTiming_public,
);
polyfillGlobal(
'PerformanceMark',
() => require('../webapis/performance/UserTiming').PerformanceMark,
);
polyfillGlobal(
'PerformanceMeasure',
() =>
require('../webapis/performance/UserTiming').PerformanceMeasure_public,
);
polyfillGlobal(
'PerformanceObserver',
() =>
require('../webapis/performance/PerformanceObserver').PerformanceObserver,
);
polyfillGlobal(
'PerformanceObserverEntryList',
() =>
require('../webapis/performance/PerformanceObserver')
.PerformanceObserverEntryList_public,
);
polyfillGlobal(
'PerformanceResourceTiming',
() =>
require('../webapis/performance/ResourceTiming')
.PerformanceResourceTiming_public,
);
polyfillGlobal(
'TaskAttributionTiming',
() =>
require('../webapis/performance/LongTasks').TaskAttributionTiming_public,
);
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {WithDefault} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type RCTActivityIndicatorViewNativeProps = Readonly<{
...ViewProps,
/**
* Whether the indicator should hide when not animating (true by default).
*
* See https://reactnative.dev/docs/activityindicator#hideswhenstopped-ios
*/
hidesWhenStopped?: WithDefault<boolean, true>,
/**
* Whether to show the indicator (true, the default) or hide it (false).
*
* See https://reactnative.dev/docs/activityindicator#animating
*/
animating?: WithDefault<boolean, true>,
/**
* The foreground color of the spinner (default is gray).
*
* See https://reactnative.dev/docs/activityindicator#color
*/
color?: ?ColorValue,
/**
* Size of the indicator (default is 'small').
* Passing a number to the size prop is only supported on Android.
*
* See https://reactnative.dev/docs/activityindicator#size
*/
size?: WithDefault<'small' | 'large', 'small'>,
}>;
export default (codegenNativeComponent<RCTActivityIndicatorViewNativeProps>(
'ActivityIndicatorView',
{
paperComponentName: 'RCTActivityIndicatorView',
},
): HostComponent<RCTActivityIndicatorViewNativeProps>);

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
DirectEventHandler,
Float,
Int32,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type DrawerStateEvent = Readonly<{
drawerState: Int32,
}>;
type DrawerSlideEvent = Readonly<{
offset: Float,
}>;
type AndroidDrawerLayoutNativeProps = Readonly<{
...ViewProps,
/**
* Determines whether the keyboard gets dismissed in response to a drag.
* - 'none' (the default), drags do not dismiss the keyboard.
* - 'on-drag', the keyboard is dismissed when a drag begins.
*/
keyboardDismissMode?: WithDefault<'none' | 'on-drag', 'none'>,
/**
* Specifies the background color of the drawer. The default value is white.
* If you want to set the opacity of the drawer, use rgba. Example:
*
* ```
* return (
* <DrawerLayoutAndroid drawerBackgroundColor="rgba(0,0,0,0.5)">
* </DrawerLayoutAndroid>
* );
* ```
*/
drawerBackgroundColor: ColorValue,
/**
* Specifies the side of the screen from which the drawer will slide in.
*/
drawerPosition?: WithDefault<'left' | 'right', 'left'>,
/**
* Specifies the width of the drawer, more precisely the width of the view that be pulled in
* from the edge of the window.
*/
drawerWidth?: WithDefault<Float, null>,
/**
* Specifies the lock mode of the drawer. The drawer can be locked in 3 states:
* - unlocked (default), meaning that the drawer will respond (open/close) to touch gestures.
* - locked-closed, meaning that the drawer will stay closed and not respond to gestures.
* - locked-open, meaning that the drawer will stay opened and not respond to gestures.
* The drawer may still be opened and closed programmatically (`openDrawer`/`closeDrawer`).
*/
drawerLockMode?: WithDefault<
'unlocked' | 'locked-closed' | 'locked-open',
'unlocked',
>,
/**
* Function called whenever there is an interaction with the navigation view.
*/
onDrawerSlide?: ?DirectEventHandler<DrawerSlideEvent>,
/**
* Function called when the drawer state has changed. The drawer can be in 3 states:
* - Idle, meaning there is no interaction with the navigation view happening at the time
* - Dragging, meaning there is currently an interaction with the navigation view
* - Settling, meaning that there was an interaction with the navigation view, and the
* navigation view is now finishing its closing or opening animation
*/
onDrawerStateChanged?: ?DirectEventHandler<DrawerStateEvent>,
/**
* Function called whenever the navigation view has been opened.
*/
onDrawerOpen?: ?DirectEventHandler<null>,
/**
* Function called whenever the navigation view has been closed.
*/
onDrawerClose?: ?DirectEventHandler<null>,
/**
* Make the drawer take the entire screen and draw the background of the
* status bar to allow it to open over the status bar. It will only have an
* effect on API 21+.
*/
statusBarBackgroundColor?: ?ColorValue,
}>;
type NativeType = HostComponent<AndroidDrawerLayoutNativeProps>;
interface NativeCommands {
+openDrawer: (viewRef: React.ElementRef<NativeType>) => void;
+closeDrawer: (viewRef: React.ElementRef<NativeType>) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['openDrawer', 'closeDrawer'],
});
export default (codegenNativeComponent<AndroidDrawerLayoutNativeProps>(
'AndroidDrawerLayout',
): NativeType);

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type AndroidHorizontalScrollContentViewNativeProps = Readonly<{
...ViewProps,
removeClippedSubviews?: ?boolean,
}>;
type NativeType = HostComponent<AndroidHorizontalScrollContentViewNativeProps>;
export default (codegenNativeComponent<AndroidHorizontalScrollContentViewNativeProps>(
'AndroidHorizontalScrollContentView',
{interfaceOnly: true},
): NativeType);

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
DirectEventHandler,
Float,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type AndroidSwipeRefreshLayoutNativeProps = Readonly<{
...ViewProps,
/**
* Whether the pull to refresh functionality is enabled.
*/
enabled?: WithDefault<boolean, true>,
/**
* The colors (at least one) that will be used to draw the refresh indicator.
*/
colors?: ?ReadonlyArray<ColorValue>,
/**
* The background color of the refresh indicator.
*/
progressBackgroundColor?: ?ColorValue,
/**
* Size of the refresh indicator.
*/
size?: WithDefault<'default' | 'large', 'default'>,
/**
* Progress view top offset
*/
progressViewOffset?: WithDefault<Float, 0>,
/**
* Called when the view starts refreshing.
*/
onRefresh?: ?DirectEventHandler<null>,
/**
* Whether the view should be indicating an active refresh.
*/
refreshing: boolean,
}>;
type NativeType = HostComponent<AndroidSwipeRefreshLayoutNativeProps>;
interface NativeCommands {
+setNativeRefreshing: (
viewRef: React.ElementRef<NativeType>,
value: boolean,
) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeRefreshing'],
});
export default (codegenNativeComponent<AndroidSwipeRefreshLayoutNativeProps>(
'AndroidSwipeRefreshLayout',
): NativeType);

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
BubblingEventHandler,
Int32,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type AndroidSwitchChangeEvent = Readonly<{
value: boolean,
target: Int32,
}>;
type AndroidSwitchNativeProps = Readonly<{
...ViewProps,
// Props
disabled?: WithDefault<boolean, false>,
enabled?: WithDefault<boolean, true>,
thumbColor?: ?ColorValue,
trackColorForFalse?: ?ColorValue,
trackColorForTrue?: ?ColorValue,
value?: WithDefault<boolean, false>,
on?: WithDefault<boolean, false>,
thumbTintColor?: ?ColorValue,
trackTintColor?: ?ColorValue,
// Events
onChange?: BubblingEventHandler<AndroidSwitchChangeEvent>,
}>;
type NativeType = HostComponent<AndroidSwitchNativeProps>;
interface NativeCommands {
+setNativeValue: (
viewRef: React.ElementRef<NativeType>,
value: boolean,
) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeValue'],
});
export default (codegenNativeComponent<AndroidSwitchNativeProps>(
'AndroidSwitch',
{
interfaceOnly: true,
},
): NativeType);

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ProcessedColorValue} from '../../../../Libraries/StyleSheet/processColor';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type DebuggingOverlayNativeProps = Readonly<{
...ViewProps,
}>;
export type DebuggingOverlayNativeComponentType =
HostComponent<DebuggingOverlayNativeProps>;
export type TraceUpdate = {
id: number,
rectangle: ElementRectangle,
color: ?ProcessedColorValue,
};
export type ElementRectangle = {
x: number,
y: number,
width: number,
height: number,
};
interface NativeCommands {
+highlightTraceUpdates: (
viewRef: React.ElementRef<DebuggingOverlayNativeComponentType>,
updates: ReadonlyArray<TraceUpdate>,
) => void;
+highlightElements: (
viewRef: React.ElementRef<DebuggingOverlayNativeComponentType>,
elements: ReadonlyArray<ElementRectangle>,
) => void;
+clearElementsHighlights: (
viewRef: React.ElementRef<DebuggingOverlayNativeComponentType>,
) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: [
'highlightTraceUpdates',
'highlightElements',
'clearElementsHighlights',
],
});
export default (codegenNativeComponent<DebuggingOverlayNativeProps>(
'DebuggingOverlay',
): HostComponent<DebuggingOverlayNativeProps>);

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
Double,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type AndroidProgressBarNativeProps = Readonly<{
...ViewProps,
//Props
styleAttr?: string,
typeAttr?: string,
indeterminate: boolean,
progress?: WithDefault<Double, 0>,
animating?: WithDefault<boolean, true>,
color?: ?ColorValue,
testID?: WithDefault<string, ''>,
}>;
export default (codegenNativeComponent<AndroidProgressBarNativeProps>(
'AndroidProgressBar',
{
interfaceOnly: true,
},
): HostComponent<AndroidProgressBarNativeProps>);

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
DirectEventHandler,
Float,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type PullToRefreshNativeProps = Readonly<{
...ViewProps,
/**
* The color of the refresh indicator.
*/
tintColor?: ?ColorValue,
/**
* Title color.
*/
titleColor?: ?ColorValue,
/**
* The title displayed under the refresh indicator.
*/
title?: WithDefault<string, null>,
/**
* Progress view top offset
*/
progressViewOffset?: WithDefault<Float, 0>,
/**
* Called when the view starts refreshing.
*/
onRefresh?: ?DirectEventHandler<null>,
/**
* Whether the view should be indicating an active refresh.
*/
refreshing: boolean,
}>;
type ComponentType = HostComponent<PullToRefreshNativeProps>;
interface NativeCommands {
+setNativeRefreshing: (
viewRef: React.ElementRef<ComponentType>,
refreshing: boolean,
) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setNativeRefreshing'],
});
export default (codegenNativeComponent<PullToRefreshNativeProps>(
'PullToRefreshView',
{
paperComponentName: 'RCTRefreshControl',
excludedPlatforms: ['android'],
},
): HostComponent<PullToRefreshNativeProps>);

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type InputAccessoryNativeProps = Readonly<{
...ViewProps,
backgroundColor?: ?ColorValue,
}>;
export default (codegenNativeComponent<InputAccessoryNativeProps>(
'InputAccessory',
{
interfaceOnly: true,
paperComponentName: 'RCTInputAccessoryView',
excludedPlatforms: ['android'],
},
): HostComponent<InputAccessoryNativeProps>);

View File

@@ -0,0 +1,156 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {
DirectEventHandler,
Int32,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type OrientationChangeEvent = Readonly<{
orientation: 'portrait' | 'landscape',
}>;
type RCTModalHostViewNativeProps = Readonly<{
...ViewProps,
/**
* The `animationType` prop controls how the modal animates.
*
* See https://reactnative.dev/docs/modal#animationtype
*/
animationType?: WithDefault<'none' | 'slide' | 'fade', 'none'>,
/**
* The `presentationStyle` prop controls how the modal appears.
*
* See https://reactnative.dev/docs/modal#presentationstyle
*/
presentationStyle?: WithDefault<
'fullScreen' | 'pageSheet' | 'formSheet' | 'overFullScreen',
'fullScreen',
>,
/**
* The `transparent` prop determines whether your modal will fill the
* entire view.
*
* See https://reactnative.dev/docs/modal#transparent
*/
transparent?: WithDefault<boolean, false>,
/**
* The `statusBarTranslucent` prop determines whether your modal should go under
* the system statusbar.
*
* See https://reactnative.dev/docs/modal#statusBarTranslucent
*/
statusBarTranslucent?: WithDefault<boolean, false>,
/**
* The `navigationBarTranslucent` prop determines whether your modal should go under
* the system navigationbar.
*
* See https://reactnative.dev/docs/modal#navigationBarTranslucent
*/
navigationBarTranslucent?: WithDefault<boolean, false>,
/**
* The `hardwareAccelerated` prop controls whether to force hardware
* acceleration for the underlying window.
*
* See https://reactnative.dev/docs/modal#hardwareaccelerated
*/
hardwareAccelerated?: WithDefault<boolean, false>,
/**
* The `onRequestClose` callback is called when the user taps the hardware
* back button on Android or the menu button on Apple TV.
*
* This is required on Apple TV and Android.
*
* See https://reactnative.dev/docs/modal#onrequestclose
*/
onRequestClose?: ?DirectEventHandler<null>,
/**
* The `onShow` prop allows passing a function that will be called once the
* modal has been shown.
*
* See https://reactnative.dev/docs/modal#onshow
*/
onShow?: ?DirectEventHandler<null>,
/**
* The `onDismiss` prop allows passing a function that will be called once
* the modal has been dismissed.
*
* See https://reactnative.dev/docs/modal#ondismiss
*/
onDismiss?: ?DirectEventHandler<null>,
/**
* The `visible` prop determines whether your modal is visible.
*
* See https://reactnative.dev/docs/modal#visible
*/
visible?: WithDefault<boolean, false>,
/**
* Deprecated. Use the `animationType` prop instead.
*/
animated?: WithDefault<boolean, false>,
/**
* Controls whether the modal can be dismissed by swiping down on iOS.
* This requires you to implement the `onRequestClose` prop to handle the dismissal.
*/
allowSwipeDismissal?: WithDefault<boolean, false>,
/**
* The `supportedOrientations` prop allows the modal to be rotated to any of the specified orientations.
*
* See https://reactnative.dev/docs/modal#supportedorientations
*/
supportedOrientations?: WithDefault<
ReadonlyArray<
| 'portrait'
| 'portrait-upside-down'
| 'landscape'
| 'landscape-left'
| 'landscape-right',
>,
'portrait',
>,
/**
* The `onOrientationChange` callback is called when the orientation changes while the modal is being displayed.
*
* See https://reactnative.dev/docs/modal#onorientationchange
*/
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,
/**
* The `identifier` is the unique number for identifying Modal components.
*/
identifier?: WithDefault<Int32, 0>,
}>;
export default (codegenNativeComponent<RCTModalHostViewNativeProps>(
'ModalHostView',
{
interfaceOnly: true,
paperComponentName: 'RCTModalHostView',
},
): HostComponent<RCTModalHostViewNativeProps>);

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type RCTSafeAreaViewNativeProps = Readonly<{
...ViewProps,
// No props
}>;
export default (codegenNativeComponent<RCTSafeAreaViewNativeProps>(
'SafeAreaView',
{
paperComponentName: 'RCTSafeAreaView',
interfaceOnly: true,
},
): HostComponent<RCTSafeAreaViewNativeProps>);

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {ColorValue} from '../../../../Libraries/StyleSheet/StyleSheet';
import type {
BubblingEventHandler,
Int32,
WithDefault,
} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeCommands from '../../../../Libraries/Utilities/codegenNativeCommands';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
import * as React from 'react';
type NativeSwitchChangeEvent = Readonly<{
value: boolean,
target: Int32,
}>;
type SwitchNativeProps = Readonly<{
...ViewProps,
// Props
disabled?: WithDefault<boolean, false>,
value?: WithDefault<boolean, false>,
tintColor?: ?ColorValue,
onTintColor?: ?ColorValue,
thumbTintColor?: ?ColorValue,
// Deprecated props
thumbColor?: ?ColorValue,
trackColorForFalse?: ?ColorValue,
trackColorForTrue?: ?ColorValue,
// Events
onChange?: ?BubblingEventHandler<NativeSwitchChangeEvent>,
}>;
type ComponentType = HostComponent<SwitchNativeProps>;
interface NativeCommands {
+setValue: (viewRef: React.ElementRef<ComponentType>, value: boolean) => void;
}
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
supportedCommands: ['setValue'],
});
export default (codegenNativeComponent<SwitchNativeProps>('Switch', {
paperComponentName: 'RCTSwitch',
excludedPlatforms: ['android'],
interfaceOnly: true,
}): ComponentType);

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type {ViewProps} from '../../../../Libraries/Components/View/ViewPropTypes';
import type {WithDefault} from '../../../../Libraries/Types/CodegenTypes';
import type {HostComponent} from '../../types/HostComponent';
import codegenNativeComponent from '../../../../Libraries/Utilities/codegenNativeComponent';
type UnimplementedNativeViewNativeProps = Readonly<{
...ViewProps,
name?: WithDefault<string, ''>,
}>;
// NOTE: This component is not implemented in paper
// Do not require this file in paper builds
export default (codegenNativeComponent<UnimplementedNativeViewNativeProps>(
'UnimplementedNativeView',
): HostComponent<UnimplementedNativeViewNativeProps>);

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+isReduceMotionEnabled: (
onSuccess: (isReduceMotionEnabled: boolean) => void,
) => void;
+isInvertColorsEnabled?: (
onSuccess: (isInvertColorsEnabled: boolean) => void,
) => void;
+isHighTextContrastEnabled?: (
onSuccess: (isHighTextContrastEnabled: boolean) => void,
) => void;
+isTouchExplorationEnabled: (
onSuccess: (isScreenReaderEnabled: boolean) => void,
) => void;
+isAccessibilityServiceEnabled?: ?(
onSuccess: (isAccessibilityServiceEnabled: boolean) => void,
) => void;
+setAccessibilityFocus: (reactTag: number) => void;
+announceForAccessibility: (announcement: string) => void;
+getRecommendedTimeoutMillis?: (
mSec: number,
onSuccess: (recommendedTimeoutMillis: number) => void,
) => void;
+isGrayscaleEnabled?: (
onSuccess: (isGrayscaleEnabled: boolean) => void,
) => void;
}
export default (TurboModuleRegistry.get<Spec>('AccessibilityInfo'): ?Spec);

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getCurrentBoldTextState: (
onSuccess: (isBoldTextEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentGrayscaleState: (
onSuccess: (isGrayscaleEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentInvertColorsState: (
onSuccess: (isInvertColorsEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentReduceMotionState: (
onSuccess: (isReduceMotionEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentDarkerSystemColorsState?: (
onSuccess: (isDarkerSystemColorsEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentPrefersCrossFadeTransitionsState?: (
onSuccess: (prefersCrossFadeTransitions: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentReduceTransparencyState: (
onSuccess: (isReduceTransparencyEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+getCurrentVoiceOverState: (
onSuccess: (isScreenReaderEnabled: boolean) => void,
onError: (error: Object) => void,
) => void;
+setAccessibilityContentSizeMultipliers: (JSMultipliers: {
+extraSmall?: ?number,
+small?: ?number,
+medium?: ?number,
+large?: ?number,
+extraLarge?: ?number,
+extraExtraLarge?: ?number,
+extraExtraExtraLarge?: ?number,
+accessibilityMedium?: ?number,
+accessibilityLarge?: ?number,
+accessibilityExtraLarge?: ?number,
+accessibilityExtraExtraLarge?: ?number,
+accessibilityExtraExtraExtraLarge?: ?number,
}) => void;
+setAccessibilityFocus: (reactTag: number) => void;
+announceForAccessibility: (announcement: string) => void;
+announceForAccessibilityWithOptions?: (
announcement: string,
options: {queue?: boolean, priority?: 'low' | 'default' | 'high'},
) => void;
}
export default (TurboModuleRegistry.get<Spec>('AccessibilityManager'): ?Spec);

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {};
+showActionSheetWithOptions: (
options: {
+title?: ?string,
+message?: ?string,
+options: ?Array<string>,
+destructiveButtonIndices?: ?Array<number>,
+cancelButtonIndex?: ?number,
+anchor?: ?number,
+tintColor?: ?number,
+cancelButtonTintColor?: ?number,
+disabledButtonTintColor?: ?number,
+userInterfaceStyle?: ?string,
+disabledButtonIndices?: Array<number>,
},
callback: (buttonIndex: number) => void,
) => void;
+showShareActionSheetWithOptions: (
options: {
+message?: ?string,
+url?: ?string,
+subject?: ?string,
+anchor?: ?number,
+tintColor?: ?number,
+cancelButtonTintColor?: ?number,
+disabledButtonTintColor?: ?number,
+excludedActivityTypes?: ?Array<string>,
+userInterfaceStyle?: ?string,
},
failureCallback: (error: {
+domain: string,
+code: string,
+userInfo?: ?Object,
+message: string,
}) => void,
successCallback: (completed: boolean, activityType: ?string) => void,
) => void;
+dismissActionSheet?: () => void;
}
export default (TurboModuleRegistry.get<Spec>('ActionSheetManager'): ?Spec);

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type Args = {
title?: string,
message?: string,
buttons?: Array<Object>, // TODO(T67565166): have a better type
type?: string,
defaultValue?: string,
cancelButtonKey?: string,
destructiveButtonKey?: string,
preferredButtonKey?: string,
keyboardType?: string,
userInterfaceStyle?: string,
};
export interface Spec extends TurboModule {
+alertWithArgs: (
args: Args,
callback: (id: number, value: string) => void,
) => void;
}
export default (TurboModuleRegistry.get<Spec>('AlertManager'): ?Spec);

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import shouldUseTurboAnimatedModule from '../../../../Libraries/Animated/shouldUseTurboAnimatedModule';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
type EndResult = {finished: boolean, value?: number, offset?: number, ...};
type EndCallback = (result: EndResult) => void;
type SaveValueCallback = (value: number) => void;
export type EventMapping = {
nativeEventPath: Array<string>,
animatedValueTag: ?number,
};
// The config has different keys depending on the type of the Node
// TODO(T54896888): Make these types strict
export type AnimatedNodeConfig = Object;
export type AnimatingNodeConfig = Object;
export interface Spec extends TurboModule {
+startOperationBatch: () => void;
+finishOperationBatch: () => void;
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
+startListeningToAnimatedNodeValue: (tag: number) => void;
+stopListeningToAnimatedNodeValue: (tag: number) => void;
+connectAnimatedNodes: (parentTag: number, childTag: number) => void;
+disconnectAnimatedNodes: (parentTag: number, childTag: number) => void;
+startAnimatingNode: (
animationId: number,
nodeTag: number,
config: AnimatingNodeConfig,
endCallback: EndCallback,
) => void;
+stopAnimation: (animationId: number) => void;
+setAnimatedNodeValue: (nodeTag: number, value: number) => void;
+setAnimatedNodeOffset: (nodeTag: number, offset: number) => void;
+flattenAnimatedNodeOffset: (nodeTag: number) => void;
+extractAnimatedNodeOffset: (nodeTag: number) => void;
+connectAnimatedNodeToView: (nodeTag: number, viewTag: number) => void;
+connectAnimatedNodeToShadowNodeFamily?: (
nodeTag: number,
shadowNode: Object,
) => void;
+disconnectAnimatedNodeFromView: (nodeTag: number, viewTag: number) => void;
+restoreDefaultValues: (nodeTag: number) => void;
+dropAnimatedNode: (tag: number) => void;
+addAnimatedEventToView: (
viewTag: number,
eventName: string,
eventMapping: EventMapping,
) => void;
+removeAnimatedEventFromView: (
viewTag: number,
eventName: string,
animatedNodeTag: number,
) => void;
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
// All of the above in a batched mode
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
}
const NativeModule: ?Spec = !shouldUseTurboAnimatedModule()
? TurboModuleRegistry.get<Spec>('NativeAnimatedModule')
: null;
export default NativeModule;

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import shouldUseTurboAnimatedModule from '../../../../Libraries/Animated/shouldUseTurboAnimatedModule';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
type EndResult = {finished: boolean, value?: number, offset?: number, ...};
type EndCallback = (result: EndResult) => void;
type SaveValueCallback = (value: number) => void;
export type EventMapping = {
nativeEventPath: Array<string>,
animatedValueTag: ?number,
};
// The config has different keys depending on the type of the Node
// TODO(T54896888): Make these types strict
export type AnimatedNodeConfig = Object;
export type AnimatingNodeConfig = Object;
export interface Spec extends TurboModule {
+startOperationBatch: () => void;
+finishOperationBatch: () => void;
+createAnimatedNode: (tag: number, config: AnimatedNodeConfig) => void;
+updateAnimatedNodeConfig?: (tag: number, config: AnimatedNodeConfig) => void;
+getValue: (tag: number, saveValueCallback: SaveValueCallback) => void;
+startListeningToAnimatedNodeValue: (tag: number) => void;
+stopListeningToAnimatedNodeValue: (tag: number) => void;
+connectAnimatedNodes: (parentTag: number, childTag: number) => void;
+disconnectAnimatedNodes: (parentTag: number, childTag: number) => void;
+startAnimatingNode: (
animationId: number,
nodeTag: number,
config: AnimatingNodeConfig,
endCallback: EndCallback,
) => void;
+stopAnimation: (animationId: number) => void;
+setAnimatedNodeValue: (nodeTag: number, value: number) => void;
+setAnimatedNodeOffset: (nodeTag: number, offset: number) => void;
+flattenAnimatedNodeOffset: (nodeTag: number) => void;
+extractAnimatedNodeOffset: (nodeTag: number) => void;
+connectAnimatedNodeToView: (nodeTag: number, viewTag: number) => void;
+disconnectAnimatedNodeFromView: (nodeTag: number, viewTag: number) => void;
+restoreDefaultValues: (nodeTag: number) => void;
+dropAnimatedNode: (tag: number) => void;
+addAnimatedEventToView: (
viewTag: number,
eventName: string,
eventMapping: EventMapping,
) => void;
+removeAnimatedEventFromView: (
viewTag: number,
eventName: string,
animatedNodeTag: number,
) => void;
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
// All of the above in a batched mode
+queueAndExecuteBatchedOperations?: (operationsAndArgs: Array<any>) => void;
}
const NativeModule: ?Spec = shouldUseTurboAnimatedModule()
? TurboModuleRegistry.get<Spec>('NativeAnimatedTurboModule')
: null;
export default NativeModule;

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type AppStateConstants = {
initialAppState: string,
};
export type AppState = {app_state: string};
export interface Spec extends TurboModule {
+getConstants: () => AppStateConstants;
+getCurrentAppState: (
success: (appState: AppState) => void,
error: (error: Object) => void,
) => void;
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('AppState'): Spec);

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type ColorSchemeName = 'light' | 'dark' | 'unspecified';
export type AppearancePreferences = {
colorScheme?: ?ColorSchemeName,
};
export interface Spec extends TurboModule {
+getColorScheme: () => ?ColorSchemeName;
+setColorScheme: (colorScheme: ColorSchemeName) => void;
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>('Appearance'): ?Spec);

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type Constants = {BLOB_URI_SCHEME: ?string, BLOB_URI_HOST: ?string};
export interface Spec extends TurboModule {
+getConstants: () => Constants;
+addNetworkingHandler: () => void;
+addWebSocketHandler: (id: number) => void;
+removeWebSocketHandler: (id: number) => void;
+sendOverSocket: (blob: Object, socketID: number) => void;
+createFromParts: (parts: Array<Object>, withId: string) => void;
+release: (blobId: string) => void;
}
const NativeModule = TurboModuleRegistry.get<Spec>('BlobModule');
let constants = null;
let NativeBlobModule = null;
if (NativeModule != null) {
NativeBlobModule = {
getConstants(): Constants {
if (constants == null) {
constants = NativeModule.getConstants();
}
return constants;
},
addNetworkingHandler(): void {
NativeModule.addNetworkingHandler();
},
addWebSocketHandler(id: number): void {
NativeModule.addWebSocketHandler(id);
},
removeWebSocketHandler(id: number): void {
NativeModule.removeWebSocketHandler(id);
},
sendOverSocket(blob: Object, socketID: number): void {
NativeModule.sendOverSocket(blob, socketID);
},
createFromParts(parts: Array<Object>, withId: string): void {
NativeModule.createFromParts(parts, withId);
},
release(blobId: string): void {
NativeModule.release(blobId);
},
};
}
export default (NativeBlobModule: ?Spec);

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {};
+getString: () => Promise<string>;
+setString: (content: string) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('Clipboard'): Spec);

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+showMessage: (
message: string,
withColor: ?number,
withBackgroundColor: ?number,
withDismissButton: ?boolean,
) => void;
+hide: () => void;
}
export default (TurboModuleRegistry.get<Spec>('DevLoadingView'): ?Spec);

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+reload: () => void;
+reloadWithReason?: (reason: string) => void;
+onFastRefresh?: () => void;
+setHotLoadingEnabled: (isHotLoadingEnabled: boolean) => void;
+setProfilingEnabled: (isProfilingEnabled: boolean) => void;
+toggleElementInspector: () => void;
+addMenuItem: (title: string) => void;
+openDebugger?: () => void;
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
// iOS only.
+setIsShakeToShowDevMenuEnabled: (enabled: boolean) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('DevSettings'): Spec);

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+invokeDefaultBackPressHandler: () => void;
}
export default (TurboModuleRegistry.get<Spec>('DeviceEventManager'): ?Spec);

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type DisplayMetricsAndroid = {
width: number,
height: number,
scale: number,
fontScale: number,
densityDpi: number,
};
export type DisplayMetrics = {
width: number,
height: number,
scale: number,
fontScale: number,
};
export type DimensionsPayload = {
window?: DisplayMetrics,
screen?: DisplayMetrics,
windowPhysicalPixels?: DisplayMetricsAndroid,
screenPhysicalPixels?: DisplayMetricsAndroid,
};
export type DeviceInfoConstants = {
+Dimensions: DimensionsPayload,
+isEdgeToEdge?: boolean,
+isIPhoneX_deprecated?: boolean,
};
export interface Spec extends TurboModule {
+getConstants: () => DeviceInfoConstants;
}
const NativeModule: Spec = TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');
let constants: ?DeviceInfoConstants = null;
const NativeDeviceInfo = {
getConstants(): DeviceInfoConstants {
if (constants == null) {
constants = NativeModule.getConstants();
}
return constants;
},
};
export default NativeDeviceInfo;

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
/* 'buttonClicked' | 'dismissed' */
type DialogAction = string;
/*
buttonPositive = -1,
buttonNegative = -2,
buttonNeutral = -3
*/
type DialogButtonKey = number;
export type DialogOptions = {
title?: string,
message?: string,
buttonPositive?: string,
buttonNegative?: string,
buttonNeutral?: string,
items?: Array<string>,
cancelable?: boolean,
};
export interface Spec extends TurboModule {
+getConstants: () => {
+buttonClicked: DialogAction,
+dismissed: DialogAction,
+buttonPositive: DialogButtonKey,
+buttonNegative: DialogButtonKey,
+buttonNeutral: DialogButtonKey,
};
+showAlert: (
config: DialogOptions,
onError: (error: string) => void,
onAction: (action: DialogAction, buttonKey?: DialogButtonKey) => void,
) => void;
}
export default (TurboModuleRegistry.get<Spec>('DialogManagerAndroid'): ?Spec);

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
const Platform = require('../../../../Libraries/Utilities/Platform').default;
export type StackFrame = {
column: ?number,
file: ?string,
lineNumber: ?number,
methodName: string,
collapse?: boolean,
};
export type ExceptionData = {
message: string,
originalMessage: ?string,
name: ?string,
componentStack: ?string,
stack: Array<StackFrame>,
id: number,
isFatal: boolean,
// flowlint-next-line unclear-type:off
extraData?: Object,
...
};
export interface Spec extends TurboModule {
// Deprecated: Use `reportException`
+reportFatalException: (
message: string,
stack: Array<StackFrame>,
exceptionId: number,
) => void;
// Deprecated: Use `reportException`
+reportSoftException: (
message: string,
stack: Array<StackFrame>,
exceptionId: number,
) => void;
+reportException?: (data: ExceptionData) => void;
// TODO(T53311281): This is a noop on iOS now. Implement it.
+dismissRedbox?: () => void;
}
const NativeModule =
TurboModuleRegistry.getEnforcing<Spec>('ExceptionsManager');
const ExceptionsManager = {
reportFatalException(
message: string,
stack: Array<StackFrame>,
exceptionId: number,
) {
NativeModule.reportFatalException(message, stack, exceptionId);
},
reportSoftException(
message: string,
stack: Array<StackFrame>,
exceptionId: number,
) {
NativeModule.reportSoftException(message, stack, exceptionId);
},
dismissRedbox(): void {
if (Platform.OS !== 'ios' && NativeModule.dismissRedbox) {
// TODO(T53311281): This is a noop on iOS now. Implement it.
NativeModule.dismissRedbox();
}
},
reportException(data: ExceptionData): void {
if (NativeModule.reportException) {
NativeModule.reportException(data);
return;
}
if (data.isFatal) {
ExceptionsManager.reportFatalException(data.message, data.stack, data.id);
} else {
ExceptionsManager.reportSoftException(data.message, data.stack, data.id);
}
},
};
export default ExceptionsManager;

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+readAsDataURL: (data: Object) => Promise<string>;
+readAsText: (data: Object, encoding: string) => Promise<string>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'FileReaderModule',
): Spec);

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+setGlobalOptions: (options: {+debug?: ?boolean}) => void;
+setContext: (context: string) => void;
+beginScroll: () => void;
+endScroll: () => void;
}
export default (TurboModuleRegistry.get<Spec>('FrameRateLogger'): ?Spec);

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+notifyTaskFinished: (taskId: number) => void;
+notifyTaskRetry: (taskId: number) => Promise<boolean>;
}
export default (TurboModuleRegistry.get<Spec>('HeadlessJsTaskSupport'): ?Spec);

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type I18nManagerConstants = {
doLeftAndRightSwapInRTL: boolean,
isRTL: boolean,
localeIdentifier?: ?string,
};
export interface Spec extends TurboModule {
+getConstants: () => I18nManagerConstants;
allowRTL: (allowRTL: boolean) => void;
forceRTL: (forceRTL: boolean) => void;
swapLeftAndRightInRTL: (flipStyles: boolean) => void;
}
export default (TurboModuleRegistry.get<Spec>('I18nManager'): ?Spec);

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
type Options = {
+offset: {
+x: number,
+y: number,
},
+size: {
+width: number,
+height: number,
},
+displaySize?: ?{
+width: number,
+height: number,
},
/**
* Enum with potential values:
* - cover
* - contain
* - stretch
* - center
* - repeat
*/
+resizeMode?: ?string,
+allowExternalStorage?: boolean,
};
export interface Spec extends TurboModule {
+getConstants: () => {};
+cropImage: (
uri: string,
cropData: Options,
successCallback: (uri: string) => void,
errorCallback: (error: string) => void,
) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'ImageEditingManager',
): Spec);

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type ImageSize = {
width: number,
height: number,
...
};
export interface Spec extends TurboModule {
+abortRequest: (requestId: number) => void;
+getConstants: () => {};
+getSize: (uri: string) => Promise<ImageSize>;
+getSizeWithHeaders: (uri: string, headers: Object) => Promise<ImageSize>;
+prefetchImage: (uri: string, requestId: number) => Promise<boolean>;
+queryCache: (uris: Array<string>) => Promise<Object>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('ImageLoader'): Spec);

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {RootTag} from '../../../../Libraries/TurboModule/RCTExport';
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {};
// Return [width, height] of image uri
+getSize: (uri: string) => Promise<ReadonlyArray<number>>;
+getSizeWithHeaders: (
uri: string,
headers: Object,
) => Promise<{
width: number,
height: number,
...
}>;
+prefetchImage: (uri: string) => Promise<boolean>;
+prefetchImageWithMetadata?: (
uri: string,
queryRootName: string,
rootTag: RootTag,
) => Promise<boolean>;
+queryCache: (uris: Array<string>) => Promise<Object>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('ImageLoader'): Spec);

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {};
+getBase64ForTag: (
uri: string,
successCallback: (base64ImageData: string) => void,
errorCallback: (error: string) => void,
) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'ImageStoreManager',
): Spec);

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {};
+getBase64ForTag: (
uri: string,
successCallback: (base64ImageData: string) => void,
errorCallback: (error: {message: string}) => void,
) => void;
+hasImageForTag: (uri: string, callback: (hasImage: boolean) => void) => void;
+removeImageForTag: (uri: string) => void;
+addImageFromBase64: (
base64ImageData: string,
successCallback: (uri: string) => void,
errorCallback: (error: {message: string}) => void,
) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'ImageStoreManager',
): Spec);

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getInitialURL: () => Promise<string>;
+canOpenURL: (url: string) => Promise<boolean>;
+openURL: (url: string) => Promise<void>;
+openSettings: () => Promise<void>;
+sendIntent: (
action: string,
extras: ?Array<{
key: string,
value: string | number | boolean, // TODO(T67672788): Union types are not type safe
...
}>,
) => Promise<void>;
}
export default (TurboModuleRegistry.get<Spec>('IntentAndroid'): ?Spec);

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+captureComplete: (path: string, error: ?string) => void;
}
export default (TurboModuleRegistry.get<Spec>('JSCHeapCapture'): ?Spec);

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>('KeyboardObserver'): ?Spec);

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
// Common interface
+getInitialURL: () => Promise<?string>;
+canOpenURL: (url: string) => Promise<boolean>;
+openURL: (url: string) => Promise<void>;
+openSettings: () => Promise<void>;
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>('LinkingManager'): ?Spec);

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+show: () => void;
+hide: () => void;
}
export default (TurboModuleRegistry.get<Spec>('LogBox'): ?Spec);

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>('ModalManager'): ?Spec);

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
type Header = [string, string];
export interface Spec extends TurboModule {
+sendRequest: (
method: string,
url: string,
requestId: number,
headers: Array<Header>,
data: Object,
responseType: string,
useIncrementalUpdates: boolean,
timeout: number,
withCredentials: boolean,
) => void;
+abortRequest: (requestId: number) => void;
+clearCookies: (callback: (result: boolean) => void) => void;
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('Networking'): Spec);

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+sendRequest: (
query: {
method: string,
url: string,
data: Object,
headers: Object,
responseType: string,
incrementalUpdates: boolean,
timeout: number,
withCredentials: boolean,
+unstable_devToolsRequestId?: string,
},
callback: (requestId: number) => void,
) => void;
+abortRequest: (requestId: number) => void;
+clearCookies: (callback: (result: boolean) => void) => void;
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('Networking'): Spec);

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
// TODO: Use proper enum types.
export type PermissionStatus = string;
export type PermissionType = string;
/*
export type PermissionStatus = 'granted' | 'denied' | 'never_ask_again';
export type PermissionType =
| 'android.permission.READ_CALENDAR'
| 'android.permission.WRITE_CALENDAR'
| 'android.permission.CAMERA'
| 'android.permission.READ_CONTACTS'
| 'android.permission.WRITE_CONTACTS'
| 'android.permission.GET_ACCOUNTS'
| 'android.permission.ACCESS_BACKGROUND_LOCATION'
| 'android.permission.ACCESS_FINE_LOCATION'
| 'android.permission.ACCESS_COARSE_LOCATION'
| 'android.permission.RECORD_AUDIO'
| 'android.permission.READ_PHONE_STATE'
| 'android.permission.CALL_PHONE'
| 'android.permission.READ_CALL_LOG'
| 'android.permission.WRITE_CALL_LOG'
| 'com.android.voicemail.permission.ADD_VOICEMAIL'
| 'com.android.voicemail.permission.READ_VOICEMAIL'
| 'com.android.voicemail.permission.WRITE_VOICEMAIL'
| 'android.permission.USE_SIP'
| 'android.permission.PROCESS_OUTGOING_CALLS'
| 'android.permission.BODY_SENSORS'
| 'android.permission.BODY_SENSORS_BACKGROUND'
| 'android.permission.SEND_SMS'
| 'android.permission.RECEIVE_SMS'
| 'android.permission.READ_SMS'
| 'android.permission.RECEIVE_WAP_PUSH'
| 'android.permission.RECEIVE_MMS'
| 'android.permission.READ_EXTERNAL_STORAGE'
| 'android.permission.READ_MEDIA_IMAGES'
| 'android.permission.READ_MEDIA_VIDEO'
| 'android.permission.READ_MEDIA_AUDIO'
| 'android.permission.READ_MEDIA_VISUAL_USER_SELECTED'
| 'android.permission.WRITE_EXTERNAL_STORAGE'
| 'android.permission.BLUETOOTH_CONNECT'
| 'android.permission.BLUETOOTH_SCAN'
| 'android.permission.BLUETOOTH_ADVERTISE'
| 'android.permission.ACCESS_MEDIA_LOCATION'
| 'android.permission.ACCEPT_HANDOVER'
| 'android.permission.ACTIVITY_RECOGNITION'
| 'android.permission.ANSWER_PHONE_CALLS'
| 'android.permission.READ_PHONE_NUMBERS'
| 'android.permission.UWB_RANGING'
| 'android.permission.POST_NOTIFICATIONS'
| 'android.permission.NEARBY_WIFI_DEVICES';
*/
export interface Spec extends TurboModule {
+checkPermission: (permission: PermissionType) => Promise<boolean>;
+requestPermission: (permission: PermissionType) => Promise<PermissionStatus>;
+shouldShowRequestPermissionRationale: (
permission: string,
) => Promise<boolean>;
+requestMultiplePermissions: (
permissions: Array<PermissionType>,
) => Promise<{[permission: PermissionType]: PermissionStatus, ...}>;
}
export default (TurboModuleRegistry.get<Spec>('PermissionsAndroid'): ?Spec);

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type ReactNativeVersionAndroid = {
major: number,
minor: number,
patch: number,
prerelease: ?string,
};
export type PlatformConstantsAndroid = {
isTesting: boolean,
isDisableAnimations?: boolean,
reactNativeVersion: ReactNativeVersionAndroid,
Version: number,
Release: string,
Serial: string,
Fingerprint: string,
Model: string,
ServerHost?: string,
uiMode: string,
Brand: string,
Manufacturer: string,
};
export interface Spec extends TurboModule {
+getConstants: () => PlatformConstantsAndroid;
+getAndroidID: () => string;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'PlatformConstants',
): Spec);

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export type PlatformConstantsIOS = {
isTesting: boolean,
isDisableAnimations?: boolean,
reactNativeVersion: {
major: number,
minor: number,
patch: number,
prerelease: ?string,
},
forceTouchAvailable: boolean,
osVersion: string,
systemName: string,
interfaceIdiom: string,
isMacCatalyst?: boolean,
};
export interface Spec extends TurboModule {
+getConstants: () => PlatformConstantsIOS;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'PlatformConstants',
): Spec);

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
type Permissions = {
alert: boolean,
badge: boolean,
sound: boolean,
};
type Notification = {
+alertTitle?: ?string,
+alertBody?: ?string,
+userInfo?: ?Object,
/**
* Identifier for the notification category. See the [Apple documentation](https://developer.apple.com/documentation/usernotifications/declaring_your_actionable_notification_types)
* for more details.
*/
+category?: ?string,
/**
* Actual type: string | number
*
* Schedule notifications using EITHER `fireDate` or `fireIntervalSeconds`.
* If both are specified, `fireDate` takes precedence.
* If you use `presentLocalNotification`, both will be ignored
* and the notification will be shown immediately.
*/
+fireDate?: ?number,
/**
* Seconds from now to display the notification.
*
* Schedule notifications using EITHER `fireDate` or `fireIntervalSeconds`.
* If both are specified, `fireDate` takes precedence.
* If you use `presentLocalNotification`, both will be ignored
* and the notification will be shown immediately.
*/
+fireIntervalSeconds?: ?number,
/** Badge count to display on the app icon. */
+applicationIconBadgeNumber?: ?number,
/** Whether to silence the notification sound. */
+isSilent?: ?boolean,
/**
* Custom notification sound. Can only be set when creating notifications.
* This will be null for notifications retrieved via
* getScheduledLocalNotifications or getDeliveredNotifications.
*/
+soundName?: ?string,
};
export interface Spec extends TurboModule {
+getConstants: () => {};
+onFinishRemoteNotification: (
notificationId: string,
/**
* Type:
* 'UIBackgroundFetchResultNewData' |
* 'UIBackgroundFetchResultNoData' |
* 'UIBackgroundFetchResultFailed'
*/
fetchResult: string,
) => void;
+setApplicationIconBadgeNumber: (num: number) => void;
+getApplicationIconBadgeNumber: (callback: (num: number) => void) => void;
+requestPermissions: (permission: {
+alert: boolean,
+badge: boolean,
+sound: boolean,
}) => Promise<Permissions>;
+abandonPermissions: () => void;
+checkPermissions: (callback: (permissions: Permissions) => void) => void;
+presentLocalNotification: (notification: Notification) => void;
+scheduleLocalNotification: (notification: Notification) => void;
+cancelAllLocalNotifications: () => void;
+cancelLocalNotifications: (userInfo: Object) => void;
+getInitialNotification: () => Promise<?Notification>;
+getScheduledLocalNotifications: (
callback: (notification: Notification) => void,
) => void;
+removeAllDeliveredNotifications: () => void;
+removeDeliveredNotifications: (identifiers: Array<string>) => void;
+getDeliveredNotifications: (
callback: (notification: Array<Notification>) => void,
) => void;
+getAuthorizationStatus: (
callback: (authorizationStatus: number) => void,
) => void;
+addListener: (eventType: string) => void;
+removeListeners: (count: number) => void;
}
export default (TurboModuleRegistry.get<Spec>(
'PushNotificationManager',
): ?Spec);

View File

@@ -0,0 +1,20 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+setExtraData: (extraData: Object, forIdentifier: string) => void;
+dismiss: () => void;
}
export default (TurboModuleRegistry.get<Spec>('RedBox'): ?Spec);

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {
RootTag,
TurboModule,
} from '../../../../Libraries/TurboModule/RCTExport';
import type {
EventEmitter,
UnsafeObject,
} from '../../../../Libraries/Types/CodegenTypes';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export enum EnumInt {
A = 23,
B = 42,
}
export type ObjectStruct = {
a: number,
b: string,
c?: ?string,
};
export interface Spec extends TurboModule {
+onPress: EventEmitter<void>;
+onClick: EventEmitter<string>;
+onChange: EventEmitter<ObjectStruct>;
+onSubmit: EventEmitter<ObjectStruct[]>;
// Exported methods.
+getConstants: () => {
const1: boolean,
const2: number,
const3: string,
};
+voidFunc: () => void;
+getBool: (arg: boolean) => boolean;
+getEnum?: (arg: EnumInt) => EnumInt;
+getNumber: (arg: number) => number;
+getString: (arg: string) => string;
+getArray: (arg: Array<any>) => Array<any>;
+getObject: (arg: Object) => Object;
+getUnsafeObject: (arg: UnsafeObject) => UnsafeObject;
+getRootTag: (arg: RootTag) => RootTag;
+getValue: (x: number, y: string, z: Object) => Object;
+getValueWithCallback: (callback: (value: string) => void) => void;
+getValueWithPromise: (error: boolean) => Promise<string>;
+voidFuncThrows?: () => void;
+getObjectThrows?: (arg: Object) => Object;
+promiseThrows?: () => Promise<void>;
+voidFuncAssert?: () => void;
+getObjectAssert?: (arg: Object) => Object;
+promiseAssert?: () => Promise<void>;
// Android-only
+getImageUrl?: () => Promise<string | null>;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'SampleTurboModule',
): Spec);

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+fetchSegment: (
segmentId: number,
options: Object, // flowlint-line unclear-type: off
callback: (error: ?Object) => void, // flowlint-line unclear-type: off
) => void;
+getSegment?: (
segmentId: number,
options: Object, // flowlint-line unclear-type: off
callback: (error: ?Object, path: ?string) => void, // flowlint-line unclear-type: off
) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>('SegmentFetcher'): Spec);

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport';
import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {
settings: Object,
};
+setValues: (values: Object) => void;
+deleteValues: (values: Array<string>) => void;
}
export default (TurboModuleRegistry.getEnforcing<Spec>(
'SettingsManager',
): Spec);

Some files were not shown because too many files have changed in this diff Show More