Merge commit 'e261f022fe6a7653b79330f878fed143725f5324' into amgleitman/0.64-merge-2020-mm-dd
This commit is contained in:
Коммит
1618e72a90
|
@ -10,13 +10,12 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const {View} = ReactNative;
|
||||
const RCTDeviceEventEmitter = require('react-native/Libraries/EventEmitter/RCTDeviceEventEmitter');
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
import NativeAccessibilityManager from 'react-native/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager';
|
||||
import invariant from 'invariant';
|
||||
import NativeAccessibilityManager from 'react-native/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager';
|
||||
import {DeviceEventEmitter, NativeModules, View} from 'react-native';
|
||||
import * as React from 'react';
|
||||
|
||||
const {TestModule} = NativeModules;
|
||||
|
||||
class AccessibilityManagerTest extends React.Component<{...}> {
|
||||
componentDidMount() {
|
||||
|
@ -39,7 +38,7 @@ class AccessibilityManagerTest extends React.Component<{...}> {
|
|||
accessibilityExtraExtraLarge: 11.0,
|
||||
accessibilityExtraExtraExtraLarge: 12.0,
|
||||
});
|
||||
RCTDeviceEventEmitter.addListener('didUpdateDimensions', update => {
|
||||
DeviceEventEmitter.addListener('didUpdateDimensions', update => {
|
||||
TestModule.markTestPassed(update.window.fontScale === 4.0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ const ReactNative = require('react-native');
|
|||
const {View} = ReactNative;
|
||||
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
import type EmitterSubscription from 'react-native/Libraries/vendor/emitter/EmitterSubscription';
|
||||
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
|
||||
|
||||
const reactViewWidth = 101;
|
||||
const reactViewHeight = 102;
|
||||
|
@ -33,7 +33,7 @@ type State = {|
|
|||
|
||||
class ReactContentSizeUpdateTest extends React.Component<Props, State> {
|
||||
_timeoutID: ?TimeoutID = null;
|
||||
_subscription: ?EmitterSubscription = null;
|
||||
_subscription: ?EventSubscription = null;
|
||||
|
||||
state: State = {
|
||||
height: reactViewHeight,
|
||||
|
|
|
@ -16,7 +16,7 @@ const ReactNative = require('react-native');
|
|||
const {View} = ReactNative;
|
||||
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
import type EmitterSubscription from 'react-native/Libraries/vendor/emitter/EmitterSubscription';
|
||||
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
|
||||
|
||||
const reactViewWidth = 111;
|
||||
const reactViewHeight = 222;
|
||||
|
@ -31,7 +31,7 @@ type Props = $ReadOnly<{|
|
|||
|}>;
|
||||
|
||||
class SizeFlexibilityUpdateTest extends React.Component<Props> {
|
||||
_subscription: ?EmitterSubscription = null;
|
||||
_subscription: ?EventSubscription = null;
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this._subscription = RCTNativeAppEventEmitter.addListener(
|
||||
|
|
|
@ -38,6 +38,15 @@ const API = {
|
|||
enableQueue: function(): void {
|
||||
queueConnections = true;
|
||||
},
|
||||
getValue: function(
|
||||
tag: number,
|
||||
saveValueCallback: (value: number) => void,
|
||||
): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
if (NativeAnimatedModule.getValue) {
|
||||
NativeAnimatedModule.getValue(tag, saveValueCallback);
|
||||
}
|
||||
},
|
||||
disableQueue: function(): void {
|
||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||
queueConnections = false;
|
||||
|
|
|
@ -15,6 +15,7 @@ import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry';
|
|||
|
||||
type EndResult = {finished: boolean, ...};
|
||||
type EndCallback = (result: EndResult) => void;
|
||||
type SaveValueCallback = (value: number) => void;
|
||||
|
||||
export type EventMapping = {|
|
||||
nativeEventPath: Array<string>,
|
||||
|
@ -28,6 +29,7 @@ export type AnimatingNodeConfig = Object;
|
|||
|
||||
export interface Spec extends TurboModule {
|
||||
+createAnimatedNode: (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;
|
||||
|
|
|
@ -36,6 +36,7 @@ describe('Native Animated', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
Object.assign(NativeAnimatedModule, {
|
||||
getValue: jest.fn(),
|
||||
addAnimatedEventToView: jest.fn(),
|
||||
connectAnimatedNodes: jest.fn(),
|
||||
connectAnimatedNodeToView: jest.fn(),
|
||||
|
@ -115,6 +116,26 @@ describe('Native Animated', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should save value on unmount', () => {
|
||||
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
|
||||
saveCallback(1);
|
||||
});
|
||||
const opacity = new Animated.Value(0);
|
||||
|
||||
opacity.__makeNative();
|
||||
|
||||
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
|
||||
const tag = opacity.__getNativeTag();
|
||||
|
||||
root.unmount();
|
||||
|
||||
expect(NativeAnimatedModule.getValue).toBeCalledWith(
|
||||
tag,
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(opacity.__getValue()).toBe(1);
|
||||
});
|
||||
|
||||
it('should extract offset', () => {
|
||||
const opacity = new Animated.Value(0);
|
||||
opacity.__makeNative();
|
||||
|
|
|
@ -86,6 +86,11 @@ class AnimatedValue extends AnimatedWithChildren {
|
|||
}
|
||||
|
||||
__detach() {
|
||||
if (this.__isNative) {
|
||||
NativeAnimatedAPI.getValue(this.__getNativeTag(), value => {
|
||||
this._value = value;
|
||||
});
|
||||
}
|
||||
this.stopAnimation();
|
||||
super.__detach();
|
||||
}
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('../vendor/emitter/EventEmitter');
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
|
||||
const invariant = require('invariant');
|
||||
const logError = require('../Utilities/logError');
|
||||
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import logError from '../Utilities/logError';
|
||||
import EventEmitter from '../vendor/emitter/EventEmitter';
|
||||
import NativeAppState from './NativeAppState';
|
||||
import invariant from 'invariant';
|
||||
|
||||
/**
|
||||
* `AppState` can tell you if the app is in the foreground or background,
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter');
|
||||
|
||||
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
|
||||
import NativeBugReporting from './NativeBugReporting';
|
||||
import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter';
|
||||
import NativeRedBox from '../NativeModules/specs/NativeRedBox';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import NativeBugReporting from './NativeBugReporting';
|
||||
|
||||
type ExtraData = {[key: string]: string, ...};
|
||||
type SourceCallback = () => string;
|
||||
|
@ -39,8 +38,8 @@ function defaultExtras() {
|
|||
class BugReporting {
|
||||
static _extraSources: Map<string, SourceCallback> = new Map();
|
||||
static _fileSources: Map<string, SourceCallback> = new Map();
|
||||
static _subscription: ?EmitterSubscription = null;
|
||||
static _redboxSubscription: ?EmitterSubscription = null;
|
||||
static _subscription: ?EventSubscription = null;
|
||||
static _redboxSubscription: ?EventSubscription = null;
|
||||
|
||||
static _maybeInit() {
|
||||
if (!BugReporting._subscription) {
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const RCTDeviceEventEmitter = require('../../EventEmitter/RCTDeviceEventEmitter');
|
||||
const UIManager = require('../../ReactNative/UIManager');
|
||||
|
||||
import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter';
|
||||
import UIManager from '../../ReactNative/UIManager';
|
||||
import NativeAccessibilityInfo from './NativeAccessibilityInfo';
|
||||
|
||||
const REDUCE_MOTION_EVENT = 'reduceMotionDidChange';
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Promise = require('../../Promise');
|
||||
const RCTDeviceEventEmitter = require('../../EventEmitter/RCTDeviceEventEmitter');
|
||||
|
||||
import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter';
|
||||
import NativeAccessibilityManager from './NativeAccessibilityManager';
|
||||
|
||||
const CHANGE_EVENT_NAME = {
|
||||
|
|
|
@ -10,14 +10,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Platform = require('../../Utilities/Platform');
|
||||
const NativeEventEmitter = require('../../EventEmitter/NativeEventEmitter');
|
||||
|
||||
import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter';
|
||||
import Platform from '../../Utilities/Platform';
|
||||
import {type EventSubscription} from '../../vendor/emitter/EventEmitter';
|
||||
import NativeTVNavigationEventEmitter from './NativeTVNavigationEventEmitter';
|
||||
import type EmitterSubscription from '../../vendor/emitter/EmitterSubscription';
|
||||
|
||||
class TVEventHandler {
|
||||
__nativeTVNavigationEventListener: ?EmitterSubscription = null;
|
||||
__nativeTVNavigationEventListener: ?EventSubscription = null;
|
||||
__nativeTVNavigationEventEmitter: ?NativeEventEmitter = null;
|
||||
|
||||
enable(component: ?any, callback: Function): void {
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const LayoutAnimation = require('../../LayoutAnimation/LayoutAnimation');
|
||||
const NativeEventEmitter = require('../../EventEmitter/NativeEventEmitter');
|
||||
|
||||
const dismissKeyboard = require('../../Utilities/dismissKeyboard');
|
||||
const invariant = require('invariant');
|
||||
|
||||
import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter';
|
||||
import LayoutAnimation from '../../LayoutAnimation/LayoutAnimation';
|
||||
import dismissKeyboard from '../../Utilities/dismissKeyboard';
|
||||
import NativeKeyboardObserver from './NativeKeyboardObserver';
|
||||
import invariant from 'invariant';
|
||||
const KeyboardEventEmitter: NativeEventEmitter = new NativeEventEmitter(
|
||||
NativeKeyboardObserver,
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@ const StyleSheet = require('../../StyleSheet/StyleSheet');
|
|||
const View = require('../View/View');
|
||||
|
||||
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet';
|
||||
import type EmitterSubscription from '../../vendor/emitter/EmitterSubscription';
|
||||
import {type EventSubscription} from '../../vendor/emitter/EventEmitter';
|
||||
import type {
|
||||
ViewProps,
|
||||
ViewLayout,
|
||||
|
@ -67,7 +67,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
_frame: ?ViewLayout = null;
|
||||
_subscriptions: Array<EmitterSubscription> = [];
|
||||
_subscriptions: Array<EventSubscription> = [];
|
||||
viewRef: {current: React.ElementRef<any> | null, ...};
|
||||
_initialFrameHeight: number = 0;
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Keyboard = require('../Keyboard');
|
||||
const dismissKeyboard = require('../../../Utilities/dismissKeyboard');
|
||||
const LayoutAnimation = require('../../../LayoutAnimation/LayoutAnimation');
|
||||
|
||||
const NativeEventEmitter = require('../../../EventEmitter/NativeEventEmitter');
|
||||
const NativeModules = require('../../../BatchedBridge/NativeModules');
|
||||
const LayoutAnimation = require('../../../LayoutAnimation/LayoutAnimation');
|
||||
const dismissKeyboard = require('../../../Utilities/dismissKeyboard');
|
||||
const Keyboard = require('../Keyboard');
|
||||
|
||||
import NativeEventEmitter from '../../../EventEmitter/NativeEventEmitter';
|
||||
|
||||
jest.mock('../../../LayoutAnimation/LayoutAnimation');
|
||||
|
||||
|
|
|
@ -10,25 +10,25 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const Dimensions = require('../Utilities/Dimensions');
|
||||
const FrameRateLogger = require('../Interaction/FrameRateLogger');
|
||||
const Keyboard = require('./Keyboard/Keyboard');
|
||||
const Platform = require('../Utilities/Platform');
|
||||
const React = require('react');
|
||||
const ReactNative = require('../Renderer/shims/ReactNative');
|
||||
const TextInputState = require('./TextInput/TextInputState');
|
||||
const UIManager = require('../ReactNative/UIManager');
|
||||
const Platform = require('../Utilities/Platform');
|
||||
import Commands from './ScrollView/ScrollViewCommands';
|
||||
|
||||
const invariant = require('invariant');
|
||||
const performanceNow = require('fbjs/lib/performanceNow');
|
||||
|
||||
import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
|
||||
import type {PressEvent, ScrollEvent} from '../Types/CoreEventTypes';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import type {KeyboardEvent} from './Keyboard/Keyboard';
|
||||
import typeof ScrollView from './ScrollView/ScrollView';
|
||||
import type {Props as ScrollViewProps} from './ScrollView/ScrollView';
|
||||
import type {KeyboardEvent} from './Keyboard/Keyboard';
|
||||
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
|
||||
import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';
|
||||
import Commands from './ScrollView/ScrollViewCommands';
|
||||
|
||||
/**
|
||||
* Mixin that can be integrated in order to handle scrolling that plays well
|
||||
|
@ -119,10 +119,10 @@ export type State = {|
|
|||
|};
|
||||
|
||||
const ScrollResponderMixin = {
|
||||
_subscriptionKeyboardWillShow: (null: ?EmitterSubscription),
|
||||
_subscriptionKeyboardWillHide: (null: ?EmitterSubscription),
|
||||
_subscriptionKeyboardDidShow: (null: ?EmitterSubscription),
|
||||
_subscriptionKeyboardDidHide: (null: ?EmitterSubscription),
|
||||
_subscriptionKeyboardWillShow: (null: ?EventSubscription),
|
||||
_subscriptionKeyboardWillHide: (null: ?EventSubscription),
|
||||
_subscriptionKeyboardDidShow: (null: ?EventSubscription),
|
||||
_subscriptionKeyboardDidHide: (null: ?EventSubscription),
|
||||
scrollResponderMixinGetInitialState: function(): State {
|
||||
return {
|
||||
isTouching: false,
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const NativeEventEmitter = require('../../EventEmitter/NativeEventEmitter');
|
||||
|
||||
import NativeEventEmitter from '../../EventEmitter/NativeEventEmitter';
|
||||
import NativeStatusBarManagerIOS from './NativeStatusBarManagerIOS';
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,8 @@ type NativeProps = $ReadOnly<{|
|
|||
backgroundColor?: ?ColorValue,
|
||||
|}>;
|
||||
|
||||
export default (codegenNativeComponent<NativeProps>(
|
||||
'RCTInputAccessoryView',
|
||||
): HostComponent<NativeProps>);
|
||||
export default (codegenNativeComponent<NativeProps>('InputAccessory', {
|
||||
interfaceOnly: true,
|
||||
paperComponentName: 'RCTInputAccessoryView',
|
||||
excludedPlatforms: ['android'],
|
||||
}): HostComponent<NativeProps>);
|
||||
|
|
|
@ -285,6 +285,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
|
|||
this.props.useForeground === true,
|
||||
),
|
||||
accessible: this.props.accessible !== false,
|
||||
accessibilityHint: this.props.accessibilityHint,
|
||||
accessibilityLabel: this.props.accessibilityLabel,
|
||||
accessibilityRole: this.props.accessibilityRole,
|
||||
accessibilityState: this.props.accessibilityState,
|
||||
|
|
|
@ -19,6 +19,12 @@ const ReactNativeViewViewConfigAndroid = {
|
|||
captured: 'onSelectCapture',
|
||||
},
|
||||
},
|
||||
topAssetDidLoad: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: 'onAssetDidLoad',
|
||||
captured: 'onAssetDidLoadCapture',
|
||||
},
|
||||
},
|
||||
},
|
||||
directEventTypes: {
|
||||
topClick: {
|
||||
|
@ -57,6 +63,9 @@ const ReactNativeViewViewConfigAndroid = {
|
|||
topSelectionChange: {
|
||||
registrationName: 'onSelectionChange',
|
||||
},
|
||||
onAssetDidLoad: {
|
||||
registrationName: 'onAssetDidLoad',
|
||||
},
|
||||
},
|
||||
validAttributes: {
|
||||
hasTVPreferredFocus: true,
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('../vendor/emitter/EventEmitter');
|
||||
const Platform = require('../Utilities/Platform');
|
||||
const RCTDeviceEventEmitter = require('./RCTDeviceEventEmitter');
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
|
||||
import Platform from '../Utilities/Platform';
|
||||
import EventEmitter from '../vendor/emitter/EventEmitter';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import RCTDeviceEventEmitter from './RCTDeviceEventEmitter';
|
||||
import invariant from 'invariant';
|
||||
|
||||
type NativeModule = {
|
||||
+addListener: (eventType: string) => void,
|
||||
|
@ -46,7 +44,7 @@ class NativeEventEmitter extends EventEmitter {
|
|||
eventType: string,
|
||||
listener: Function,
|
||||
context: ?Object,
|
||||
): EmitterSubscription {
|
||||
): EventSubscription {
|
||||
if (this._nativeModule != null) {
|
||||
this._nativeModule.addListener(eventType);
|
||||
}
|
||||
|
@ -62,7 +60,7 @@ class NativeEventEmitter extends EventEmitter {
|
|||
super.removeAllListeners(eventType);
|
||||
}
|
||||
|
||||
removeSubscription(subscription: EmitterSubscription) {
|
||||
removeSubscription(subscription: EventSubscription) {
|
||||
if (this._nativeModule != null) {
|
||||
this._nativeModule.removeListeners(1);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('../vendor/emitter/EventEmitter');
|
||||
const EventSubscriptionVendor = require('../vendor/emitter/EventSubscriptionVendor');
|
||||
|
||||
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
|
||||
import EventEmitter from '../vendor/emitter/EventEmitter';
|
||||
import type EmitterSubscription from '../vendor/emitter/_EmitterSubscription';
|
||||
import EventSubscriptionVendor from '../vendor/emitter/_EventSubscriptionVendor';
|
||||
|
||||
function checkNativeEventModule(eventType: ?string) {
|
||||
if (eventType) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const RCTDeviceEventEmitter = require('./RCTDeviceEventEmitter');
|
||||
import RCTDeviceEventEmitter from './RCTDeviceEventEmitter';
|
||||
|
||||
/**
|
||||
* Deprecated - subclass NativeEventEmitter to create granular event modules instead of
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('../../vendor/emitter/EventEmitter');
|
||||
const RCTDeviceEventEmitter = require('../RCTDeviceEventEmitter');
|
||||
import EventEmitter from '../../vendor/emitter/EventEmitter';
|
||||
import RCTDeviceEventEmitter from '../RCTDeviceEventEmitter';
|
||||
|
||||
/**
|
||||
* Mock the NativeEventEmitter as a normal JS EventEmitter.
|
||||
|
|
|
@ -233,6 +233,10 @@ namespace facebook {
|
|||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "createAnimatedNode", @selector(createAnimatedNode:config:), args, count);
|
||||
}
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "getValue", @selector(getValue:saveValueCallback:), args, count);
|
||||
}
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "startListeningToAnimatedNodeValue", @selector(startListeningToAnimatedNodeValue:), args, count);
|
||||
}
|
||||
|
@ -312,6 +316,9 @@ namespace facebook {
|
|||
methodMap_["createAnimatedNode"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_createAnimatedNode};
|
||||
|
||||
|
||||
methodMap_["getValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_getValue};
|
||||
|
||||
|
||||
methodMap_["startListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue};
|
||||
|
||||
|
||||
|
|
|
@ -276,6 +276,8 @@ namespace JS {
|
|||
|
||||
- (void)createAnimatedNode:(double)tag
|
||||
config:(NSDictionary *)config;
|
||||
- (void)getValue:(double)tag
|
||||
saveValueCallback:(RCTResponseSenderBlock)saveValueCallback;
|
||||
- (void)startListeningToAnimatedNodeValue:(double)tag;
|
||||
- (void)stopListeningToAnimatedNodeValue:(double)tag;
|
||||
- (void)connectAnimatedNodes:(double)parentTag
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
'use strict';
|
||||
|
||||
const BatchedBridge = require('../BatchedBridge/BatchedBridge');
|
||||
const EventEmitter = require('../vendor/emitter/EventEmitter');
|
||||
const TaskQueue = require('./TaskQueue');
|
||||
|
||||
const infoLog = require('../Utilities/infoLog');
|
||||
const invariant = require('invariant');
|
||||
const keyMirror = require('fbjs/lib/keyMirror');
|
||||
|
||||
import EventEmitter from '../vendor/emitter/EventEmitter';
|
||||
|
||||
export type Handle = number;
|
||||
import type {Task} from './TaskQueue';
|
||||
|
||||
|
|
|
@ -10,13 +10,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const InteractionManager = require('../Interaction/InteractionManager');
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
const Platform = require('../Utilities/Platform');
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import InteractionManager from '../Interaction/InteractionManager';
|
||||
import Platform from '../Utilities/Platform';
|
||||
import NativeLinking from './NativeLinking';
|
||||
import invariant from 'invariant';
|
||||
|
||||
/**
|
||||
* `Linking` gives you a general interface to interact with both incoming
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
'use strict';
|
||||
|
||||
const AppContainer = require('../ReactNative/AppContainer');
|
||||
const {RootTagContext} = require('../ReactNative/RootTag');
|
||||
const I18nManager = require('../ReactNative/I18nManager');
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
import NativeModalManager from './NativeModalManager';
|
||||
|
@ -22,12 +21,14 @@ const ScrollView = require('../Components/ScrollView/ScrollView');
|
|||
const StyleSheet = require('../StyleSheet/StyleSheet');
|
||||
const View = require('../Components/View/View');
|
||||
|
||||
import type {RootTag} from '../ReactNative/RootTag';
|
||||
const {RootTagContext} = require('../ReactNative/RootTag');
|
||||
|
||||
import type {ViewProps} from '../Components/View/ViewPropTypes';
|
||||
import type {DirectEventHandler} from '../Types/CodegenTypes';
|
||||
import type EmitterSubscription from '../vendor/emitter/EmitterSubscription';
|
||||
import RCTModalHostView from './RCTModalHostViewNativeComponent';
|
||||
import {VirtualizedListContextResetter} from '../Lists/VirtualizedListContext.js';
|
||||
import type {RootTag} from '../ReactNative/RootTag';
|
||||
import type {DirectEventHandler} from '../Types/CodegenTypes';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import RCTModalHostView from './RCTModalHostViewNativeComponent';
|
||||
|
||||
const ModalEventEmitter =
|
||||
Platform.OS === 'ios' && NativeModalManager != null
|
||||
|
@ -161,7 +162,7 @@ class Modal extends React.Component<Props> {
|
|||
static contextType: React.Context<RootTag> = RootTagContext;
|
||||
|
||||
_identifier: number;
|
||||
_eventSubscription: ?EmitterSubscription;
|
||||
_eventSubscription: ?EventSubscription;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
|
|
@ -216,6 +216,12 @@ RCT_EXPORT_METHOD(removeAnimatedEventFromView:(double)viewTag
|
|||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getValue:(double)nodeTag saveCallback:(RCTResponseSenderBlock)saveCallback) {
|
||||
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
||||
[nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveCallback];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -- Batch handling
|
||||
|
||||
- (void)addOperationBlock:(AnimatedOperation)operation
|
||||
|
@ -300,7 +306,6 @@ RCT_EXPORT_METHOD(removeAnimatedEventFromView:(double)viewTag
|
|||
operation(self->_nodesManager);
|
||||
}
|
||||
}];
|
||||
|
||||
[uiManager addUIBlock:^(__unused RCTUIManager *manager, __unused NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS ISS#3536887)
|
||||
for (AnimatedOperation operation in operations) {
|
||||
operation(self->_nodesManager);
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
|
||||
- (BOOL)isNodeManagedByFabric:(nonnull NSNumber *)tag;
|
||||
|
||||
- (void)getValue:(nonnull NSNumber *)nodeTag
|
||||
saveCallback:(nullable RCTResponseSenderBlock)saveCallback;
|
||||
|
||||
// graph
|
||||
|
||||
- (void)createAnimatedNode:(nonnull NSNumber *)tag
|
||||
|
|
|
@ -242,6 +242,17 @@ static NSString *RCTNormalizeAnimatedEventName(NSString *eventName)
|
|||
[valueNode extractOffset];
|
||||
}
|
||||
|
||||
- (void)getValue:(NSNumber *)nodeTag saveCallback:(RCTResponseSenderBlock)saveCallback
|
||||
{
|
||||
RCTAnimatedNode *node = _animationNodes[nodeTag];
|
||||
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
|
||||
RCTLogError(@"Not a value node.");
|
||||
return;
|
||||
}
|
||||
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;;
|
||||
saveCallback(@[@(valueNode.value)]);
|
||||
}
|
||||
|
||||
#pragma mark -- Drivers
|
||||
|
||||
- (void)startAnimatingNode:(nonnull NSNumber *)animationId
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
|
||||
// Do not require the native RCTNetworking module directly! Use this wrapper module instead.
|
||||
// It will add the necessary requestId, so that you don't have to generate it yourself.
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
const convertRequestBody = require('./convertRequestBody');
|
||||
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import NativeNetworkingAndroid from './NativeNetworkingAndroid';
|
||||
import convertRequestBody from './convertRequestBody';
|
||||
import type {RequestBody} from './convertRequestBody';
|
||||
|
||||
type Header = [string, string];
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
|
||||
const convertRequestBody = require('./convertRequestBody');
|
||||
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import NativeNetworkingIOS from './NativeNetworkingIOS';
|
||||
import type {NativeResponseType} from './XMLHttpRequest';
|
||||
import convertRequestBody from './convertRequestBody';
|
||||
import type {RequestBody} from './convertRequestBody';
|
||||
|
||||
class RCTNetworking extends NativeEventEmitter {
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
import {Platform} from 'react-native'; // TODO(macOS GH#774)
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import NativePushNotificationManagerIOS from './NativePushNotificationManagerIOS';
|
||||
const invariant = require('invariant');
|
||||
import invariant from 'invariant';
|
||||
|
||||
const PushNotificationEmitter = new NativeEventEmitter(
|
||||
NativePushNotificationManagerIOS,
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EmitterSubscription = require('../vendor/emitter/EmitterSubscription');
|
||||
const PropTypes = require('prop-types');
|
||||
const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter');
|
||||
const React = require('react');
|
||||
import View from '../Components/View/View';
|
||||
import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter';
|
||||
import StyleSheet from '../StyleSheet/StyleSheet';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import {RootTagContext, createRootTag} from './RootTag';
|
||||
const StyleSheet = require('../StyleSheet/StyleSheet');
|
||||
const View = require('../Components/View/View');
|
||||
import PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
|
||||
type Context = {rootTag: number, ...};
|
||||
|
||||
|
@ -43,7 +43,7 @@ class AppContainer extends React.Component<Props, State> {
|
|||
hasError: false,
|
||||
};
|
||||
_mainRef: ?React.ElementRef<typeof View>;
|
||||
_subscription: ?EmitterSubscription = null;
|
||||
_subscription: ?EventSubscription = null;
|
||||
|
||||
static getDerivedStateFromError: any = undefined;
|
||||
|
||||
|
|
|
@ -10,11 +10,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const RCTDeviceEventEmitter = require('../EventEmitter/RCTDeviceEventEmitter');
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter';
|
||||
import NativeSettingsManager from './NativeSettingsManager';
|
||||
import invariant from 'invariant';
|
||||
|
||||
const subscriptions: Array<{
|
||||
keys: Array<string>,
|
||||
|
|
|
@ -69,6 +69,7 @@ const viewConfig = {
|
|||
onInlineViewLayout: true,
|
||||
dataDetectorType: true,
|
||||
tooltip: true,
|
||||
android_hyphenationFrequency: true,
|
||||
},
|
||||
directEventTypes: {
|
||||
topTextLayout: {
|
||||
|
|
|
@ -45,6 +45,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// it's declared here only to conform to the interface.
|
||||
@property (nonatomic, assign) BOOL caretHidden;
|
||||
|
||||
@property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;
|
||||
|
||||
#if TARGET_OS_OSX // [TODO(macOS GH#774)
|
||||
@property (nonatomic, assign) BOOL scrollEnabled;
|
||||
@property (nonatomic, strong, nullable) RCTUIColor *selectionColor; // TODO(OSS Candidate ISS#2710739)
|
||||
|
|
|
@ -55,6 +55,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
|
||||
#endif // TODO(macOS GH#774)
|
||||
@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
|
||||
@property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;
|
||||
|
||||
// This protocol disallows direct access to `selectedTextRange` property because
|
||||
// unwise usage of it can break the `delegate` behavior. So, we always have to
|
||||
|
|
|
@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (assign, getter=isEditable) BOOL editable;
|
||||
#endif // ]TODO(macOS GH#774)
|
||||
@property (nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
|
||||
@property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;
|
||||
|
||||
#if TARGET_OS_OSX // [TODO(macOS GH#774)
|
||||
@property (nonatomic, copy, nullable) NSString *text;
|
||||
|
|
|
@ -57,6 +57,18 @@ export type TextProps = $ReadOnly<{|
|
|||
* See https://reactnative.dev/docs/text.html#allowfontscaling
|
||||
*/
|
||||
allowFontScaling?: ?boolean,
|
||||
|
||||
/**
|
||||
* Set hyphenation strategy on Android.
|
||||
*
|
||||
*/
|
||||
android_hyphenationFrequency?: ?(
|
||||
| 'normal'
|
||||
| 'none'
|
||||
| 'full'
|
||||
| 'high'
|
||||
| 'balanced'
|
||||
),
|
||||
children?: ?Node,
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,18 +10,16 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Blob = require('../Blob/Blob');
|
||||
const BlobManager = require('../Blob/BlobManager');
|
||||
const EventTarget = require('event-target-shim');
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
const WebSocketEvent = require('./WebSocketEvent');
|
||||
|
||||
const base64 = require('base64-js');
|
||||
const binaryToBase64 = require('../Utilities/binaryToBase64');
|
||||
const invariant = require('invariant');
|
||||
|
||||
import type EventSubscription from '../vendor/emitter/EventSubscription';
|
||||
import Blob from '../Blob/Blob';
|
||||
import BlobManager from '../Blob/BlobManager';
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import binaryToBase64 from '../Utilities/binaryToBase64';
|
||||
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
|
||||
import NativeWebSocketModule from './NativeWebSocketModule';
|
||||
import WebSocketEvent from './WebSocketEvent';
|
||||
import base64 from 'base64-js';
|
||||
import EventTarget from 'event-target-shim';
|
||||
import invariant from 'invariant';
|
||||
|
||||
type ArrayBufferView =
|
||||
| Int8Array
|
||||
|
|
|
@ -9,11 +9,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const NativeEventEmitter = require('../EventEmitter/NativeEventEmitter');
|
||||
|
||||
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter';
|
||||
import NativeWebSocketModule from './NativeWebSocketModule';
|
||||
|
||||
const base64 = require('base64-js');
|
||||
import base64 from 'base64-js';
|
||||
|
||||
const originalRCTWebSocketConnect = NativeWebSocketModule.connect;
|
||||
const originalRCTWebSocketSend = NativeWebSocketModule.send;
|
||||
|
|
|
@ -580,16 +580,7 @@ if (global.nativeLoggingHook) {
|
|||
const reactNativeMethod = console[methodName];
|
||||
if (originalConsole[methodName]) {
|
||||
console[methodName] = function() {
|
||||
// TODO(T43930203): remove this special case once originalConsole.assert properly checks
|
||||
// the condition
|
||||
if (methodName === 'assert') {
|
||||
if (!arguments[0] && originalConsole.hasOwnProperty('assert')) {
|
||||
// TODO(macOS GH#774)
|
||||
originalConsole.assert(...arguments);
|
||||
}
|
||||
} else {
|
||||
originalConsole[methodName](...arguments);
|
||||
}
|
||||
originalConsole[methodName](...arguments);
|
||||
reactNativeMethod.apply(console, arguments);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,225 +4,17 @@
|
|||
* 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
|
||||
* @noflow
|
||||
* @typecheck
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const EmitterSubscription = require('./EmitterSubscription');
|
||||
const EventSubscriptionVendor = require('./EventSubscriptionVendor');
|
||||
// $FlowFixMe - EventEmitter Type Safety
|
||||
const EventEmitter = require('./_EventEmitter');
|
||||
|
||||
const invariant = require('invariant');
|
||||
export default EventEmitter;
|
||||
|
||||
const sparseFilterPredicate = () => true;
|
||||
|
||||
/**
|
||||
* @class EventEmitter
|
||||
* @description
|
||||
* An EventEmitter is responsible for managing a set of listeners and publishing
|
||||
* events to them when it is told that such events happened. In addition to the
|
||||
* data for the given event it also sends a event control object which allows
|
||||
* the listeners/handlers to prevent the default behavior of the given event.
|
||||
*
|
||||
* The emitter is designed to be generic enough to support all the different
|
||||
* contexts in which one might want to emit events. It is a simple multicast
|
||||
* mechanism on top of which extra functionality can be composed. For example, a
|
||||
* more advanced emitter may use an EventHolder and EventFactory.
|
||||
*/
|
||||
class EventEmitter {
|
||||
_subscriber: EventSubscriptionVendor;
|
||||
_currentSubscription: ?EmitterSubscription;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
|
||||
* to use. If omitted, a new subscriber will be created for the emitter.
|
||||
*/
|
||||
constructor(subscriber: ?EventSubscriptionVendor) {
|
||||
this._subscriber = subscriber || new EventSubscriptionVendor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be invoked when events of the specified type are
|
||||
* emitted. An optional calling context may be provided. The data arguments
|
||||
* emitted will be passed to the listener function.
|
||||
*
|
||||
* TODO: Annotate the listener arg's type. This is tricky because listeners
|
||||
* can be invoked with varargs.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
addListener(
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
context: ?Object,
|
||||
): EmitterSubscription {
|
||||
return (this._subscriber.addSubscription(
|
||||
eventType,
|
||||
new EmitterSubscription(this, this._subscriber, listener, context),
|
||||
): any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to addListener, except that the listener is removed after it is
|
||||
* invoked once.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke only once when the
|
||||
* specified event is emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
once(
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
context: ?Object,
|
||||
): EmitterSubscription {
|
||||
return this.addListener(eventType, (...args) => {
|
||||
this.removeCurrentListener();
|
||||
listener.apply(context, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the registered listeners, including those registered as
|
||||
* listener maps.
|
||||
*
|
||||
* @param {?string} eventType - Optional name of the event whose registered
|
||||
* listeners to remove
|
||||
*/
|
||||
removeAllListeners(eventType: ?string) {
|
||||
this._subscriber.removeAllSubscriptions(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API that can be called during an eventing cycle to remove the
|
||||
* last listener that was invoked. This allows a developer to provide an event
|
||||
* object that can remove the listener (or listener map) during the
|
||||
* invocation.
|
||||
*
|
||||
* If it is called when not inside of an emitting cycle it will throw.
|
||||
*
|
||||
* @throws {Error} When called not during an eventing cycle
|
||||
*
|
||||
* @example
|
||||
* var subscription = emitter.addListenerMap({
|
||||
* someEvent: function(data, event) {
|
||||
* console.log(data);
|
||||
* emitter.removeCurrentListener();
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* emitter.emit('someEvent', 'abc'); // logs 'abc'
|
||||
* emitter.emit('someEvent', 'def'); // does not log anything
|
||||
*/
|
||||
removeCurrentListener() {
|
||||
invariant(
|
||||
!!this._currentSubscription,
|
||||
'Not in an emitting cycle; there is no current subscription',
|
||||
);
|
||||
this.removeSubscription(this._currentSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific subscription. Called by the `remove()` method of the
|
||||
* subscription itself to ensure any necessary cleanup is performed.
|
||||
*/
|
||||
removeSubscription(subscription: EmitterSubscription) {
|
||||
invariant(
|
||||
subscription.emitter === this,
|
||||
'Subscription does not belong to this emitter.',
|
||||
);
|
||||
this._subscriber.removeSubscription(subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of listeners that are currently registered for the given
|
||||
* event.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to query
|
||||
* @returns {array}
|
||||
*/
|
||||
listeners(eventType: string): [EmitterSubscription] {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
return subscriptions
|
||||
? subscriptions
|
||||
// We filter out missing entries because the array is sparse.
|
||||
// "callbackfn is called only for elements of the array which actually
|
||||
// exist; it is not called for missing elements of the array."
|
||||
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.filter
|
||||
.filter(sparseFilterPredicate)
|
||||
.map(subscription => subscription.listener)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of the given type with the given data. All handlers of that
|
||||
* particular type will be notified.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||||
*
|
||||
* @example
|
||||
* emitter.addListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* });
|
||||
*
|
||||
* emitter.emit('someEvent', 'abc'); // logs 'abc'
|
||||
*/
|
||||
emit(eventType: string) {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
if (subscription && subscription.listener) {
|
||||
this._currentSubscription = subscription;
|
||||
subscription.listener.apply(
|
||||
subscription.context,
|
||||
Array.prototype.slice.call(arguments, 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
this._currentSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given listener for event of specific type.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
*
|
||||
* @example
|
||||
* emitter.removeListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* }); // removes the listener if already registered
|
||||
*
|
||||
*/
|
||||
removeListener(eventType: String, listener) {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
// its listener matches the listener in method parameters
|
||||
if (subscription && subscription.listener === listener) {
|
||||
subscription.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export interface EventSubscription {
|
||||
remove(): void;
|
||||
}
|
||||
|
||||
module.exports = EventEmitter;
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const EventSubscription = require('./EventSubscription');
|
||||
|
||||
import type EventEmitter from './EventEmitter';
|
||||
import type EventSubscriptionVendor from './EventSubscriptionVendor';
|
||||
import EventSubscription from './_EventSubscription';
|
||||
import type EventSubscriptionVendor from './_EventSubscriptionVendor';
|
||||
|
||||
/**
|
||||
* EmitterSubscription represents a subscription with listener and context data.
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @noflow
|
||||
* @typecheck
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const invariant = require('invariant');
|
||||
|
||||
import EmitterSubscription from './_EmitterSubscription';
|
||||
import EventSubscriptionVendor from './_EventSubscriptionVendor';
|
||||
|
||||
const sparseFilterPredicate = () => true;
|
||||
|
||||
/**
|
||||
* @class EventEmitter
|
||||
* @description
|
||||
* An EventEmitter is responsible for managing a set of listeners and publishing
|
||||
* events to them when it is told that such events happened. In addition to the
|
||||
* data for the given event it also sends a event control object which allows
|
||||
* the listeners/handlers to prevent the default behavior of the given event.
|
||||
*
|
||||
* The emitter is designed to be generic enough to support all the different
|
||||
* contexts in which one might want to emit events. It is a simple multicast
|
||||
* mechanism on top of which extra functionality can be composed. For example, a
|
||||
* more advanced emitter may use an EventHolder and EventFactory.
|
||||
*/
|
||||
class EventEmitter {
|
||||
_subscriber: EventSubscriptionVendor;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
|
||||
* to use. If omitted, a new subscriber will be created for the emitter.
|
||||
*/
|
||||
constructor(subscriber: ?EventSubscriptionVendor) {
|
||||
this._subscriber = subscriber || new EventSubscriptionVendor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to be invoked when events of the specified type are
|
||||
* emitted. An optional calling context may be provided. The data arguments
|
||||
* emitted will be passed to the listener function.
|
||||
*
|
||||
* TODO: Annotate the listener arg's type. This is tricky because listeners
|
||||
* can be invoked with varargs.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to listen to
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
* @param {*} context - Optional context object to use when invoking the
|
||||
* listener
|
||||
*/
|
||||
addListener(
|
||||
eventType: string,
|
||||
listener: Function,
|
||||
context: ?Object,
|
||||
): EmitterSubscription {
|
||||
return (this._subscriber.addSubscription(
|
||||
eventType,
|
||||
new EmitterSubscription(this, this._subscriber, listener, context),
|
||||
): any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the registered listeners, including those registered as
|
||||
* listener maps.
|
||||
*
|
||||
* @param {?string} eventType - Optional name of the event whose registered
|
||||
* listeners to remove
|
||||
*/
|
||||
removeAllListeners(eventType: ?string) {
|
||||
this._subscriber.removeAllSubscriptions(eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific subscription. Called by the `remove()` method of the
|
||||
* subscription itself to ensure any necessary cleanup is performed.
|
||||
*/
|
||||
removeSubscription(subscription: EmitterSubscription) {
|
||||
invariant(
|
||||
subscription.emitter === this,
|
||||
'Subscription does not belong to this emitter.',
|
||||
);
|
||||
this._subscriber.removeSubscription(subscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of listeners that are currently registered for the given
|
||||
* event.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to query
|
||||
* @returns {array}
|
||||
*/
|
||||
listeners(eventType: string): [EmitterSubscription] {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
return subscriptions
|
||||
? subscriptions
|
||||
// We filter out missing entries because the array is sparse.
|
||||
// "callbackfn is called only for elements of the array which actually
|
||||
// exist; it is not called for missing elements of the array."
|
||||
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.filter
|
||||
.filter(sparseFilterPredicate)
|
||||
.map(subscription => subscription.listener)
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of the given type with the given data. All handlers of that
|
||||
* particular type will be notified.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||||
*
|
||||
* @example
|
||||
* emitter.addListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* });
|
||||
*
|
||||
* emitter.emit('someEvent', 'abc'); // logs 'abc'
|
||||
*/
|
||||
emit(eventType: string) {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
if (subscription && subscription.listener) {
|
||||
subscription.listener.apply(
|
||||
subscription.context,
|
||||
Array.prototype.slice.call(arguments, 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given listener for event of specific type.
|
||||
*
|
||||
* @param {string} eventType - Name of the event to emit
|
||||
* @param {function} listener - Function to invoke when the specified event is
|
||||
* emitted
|
||||
*
|
||||
* @example
|
||||
* emitter.removeListener('someEvent', function(message) {
|
||||
* console.log(message);
|
||||
* }); // removes the listener if already registered
|
||||
*
|
||||
*/
|
||||
removeListener(eventType: String, listener) {
|
||||
const subscriptions = this._subscriber.getSubscriptionsForType(eventType);
|
||||
if (subscriptions) {
|
||||
for (let i = 0, l = subscriptions.length; i < l; i++) {
|
||||
const subscription = subscriptions[i];
|
||||
|
||||
// The subscription may have been removed during this event loop.
|
||||
// its listener matches the listener in method parameters
|
||||
if (subscription && subscription.listener === listener) {
|
||||
subscription.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventEmitter;
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import type EventSubscriptionVendor from './EventSubscriptionVendor';
|
||||
import type EventSubscriptionVendor from './_EventSubscriptionVendor';
|
||||
|
||||
/**
|
||||
* EventSubscription represents a subscription to a particular event. It can
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
const invariant = require('invariant');
|
||||
|
||||
import type EventSubscription from './EventSubscription';
|
||||
import type EventSubscription from './_EventSubscription';
|
||||
|
||||
/**
|
||||
* EventSubscriptionVendor stores a set of EventSubscriptions that are
|
|
@ -524,8 +524,8 @@ SPEC CHECKSUMS:
|
|||
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
|
||||
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
|
||||
DoubleConversion: 2b45d0f8e156a5b02354c8a4062de64d41ccb4e0
|
||||
FBLazyVector: 98b31cc648baaf777f631c1faa014adfb111dd36
|
||||
FBReactNativeSpec: bf2e6b17d4cfb71670a7eb78db955af21e923d76
|
||||
FBLazyVector: 25db8fc930c080720dd9f1d1a26d539ce9515858
|
||||
FBReactNativeSpec: 357b56d66b8d511a9f812b4c630bd4966a433371
|
||||
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
|
||||
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
|
||||
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
|
||||
|
@ -536,34 +536,34 @@ SPEC CHECKSUMS:
|
|||
glog: 789873d01e4b200777d0a09bc23d548446758699
|
||||
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
||||
RCT-Folly: 55d0039b24e192081ec0b2257f7bd9f42e382fb7
|
||||
RCTRequired: a6c49a02ff5189dfeaf791a51d21064766344e72
|
||||
RCTTypeSafety: 6bb50d00867266b0d061b94f5e225b9945882688
|
||||
React: 78f1070302eae565a18c64e9d8052ed223768640
|
||||
React-ART: a5ab7ec5b4576cf43e262809a93d42ee7e8413bf
|
||||
React-callinvoker: d223e8b29d68bc2238b878501861d8ba24fce52e
|
||||
React-Core: af3201d391ab6aa22eb000f2e6a0e206f2c3888c
|
||||
React-CoreModules: 5cd8929ac73b3f5606cf3b5b0821ece53eafff8b
|
||||
React-cxxreact: 4fa8629ade061a82ea45de0c4b87d479e48adb68
|
||||
React-jsi: dc6f1e92c022648c355c23e1949cff6607f9cbfd
|
||||
React-jsiexecutor: 3b7454c42768f05ed3cc37e6f895178763976b9c
|
||||
React-jsinspector: d2982fce9882b572b4d09460c001697b0cbcac26
|
||||
React-perflogger: fb60383b80547c8ce8fa5d8984447b09db21c5cd
|
||||
React-RCTActionSheet: 3b37e04729f1cbc38167daa61c0a56ec88d80c98
|
||||
React-RCTAnimation: b25e66f6f885a28a329ca4cd7f1756f7e7955b9f
|
||||
React-RCTBlob: 347583bec23dfc93b38efe963c02083ba6cf4e11
|
||||
React-RCTImage: 33b8c6a26efde67f1a37422778d847b1970fc178
|
||||
React-RCTLinking: 25f85db00b40a22daa03bb0423a029329a545d44
|
||||
React-RCTNetwork: 4acf13ffdc4b65468838d833256a69c7f428281e
|
||||
React-RCTPushNotification: de80429639189142d77281feb5f72c68b1c54766
|
||||
React-RCTSettings: a454a15306ce3db6e3c1b101b0b3cc5b79786896
|
||||
React-RCTTest: a407d26fd93dce440fd9b991936f2ebb01535244
|
||||
React-RCTText: 4d6d446fb541e5d851bc37b218da70938a29bfaf
|
||||
React-RCTVibration: 27e8099fa8ece2cc97f2abd016c1b06aacb75912
|
||||
React-runtimeexecutor: 8e9720ed1a7c405f81bd399f2a3247bbcabe1d04
|
||||
RCTRequired: ce43f9f9167af58d0e1eecf5605634ab016faa81
|
||||
RCTTypeSafety: c9b0498fb46d5d1d756e0ce90dd5e60f1b9d5cdd
|
||||
React: 9a7fd7a316d91eaabfb9b6fbe3456d873e1d92b2
|
||||
React-ART: c2a752357958d214b16806c18fd812e3eda5165e
|
||||
React-callinvoker: 66b62bab1170e0359d46a8af0cb61a9a1a8d18ad
|
||||
React-Core: 312e62d070297a0cf4c693eb9400145149b1fc70
|
||||
React-CoreModules: 68d668bfd30a500e49c83c68e2fc75524f2437f8
|
||||
React-cxxreact: 6e2e98c6b87bc02437806a9498c582a03a10c9f5
|
||||
React-jsi: 827382a7da620a02cb890accfcf84de279f272a6
|
||||
React-jsiexecutor: dbc72f3190c3f3bf7d2ac548ce17e12d357e122b
|
||||
React-jsinspector: 320d5cb76361b00a6a87a8b5a67280c200712604
|
||||
React-perflogger: 0cac631583b97716913aafd5d5c07ac133bab771
|
||||
React-RCTActionSheet: c15c33157199c398d405a5cbc2715d9dba17bbf8
|
||||
React-RCTAnimation: bffa5450d36cae700b6c97bfdd617986810b6f33
|
||||
React-RCTBlob: 169d4c1f011c5fe60f43cbde36f25391c5bd62a7
|
||||
React-RCTImage: 3a129461152c25115f67a3052e086b8277c36938
|
||||
React-RCTLinking: 2f4d467f6449bfa6f35ef108ea4eada0c110e020
|
||||
React-RCTNetwork: 66bdde9026a6fe29b06b6027b847de96153653a7
|
||||
React-RCTPushNotification: c2b605a89e385917bb764017d0e8e5c6d4f4a206
|
||||
React-RCTSettings: 8d536a656c3d2f34ea155d24244488a0c39ec3b2
|
||||
React-RCTTest: 991feac2784e473e8b466091259cefe6d33bc72f
|
||||
React-RCTText: 6be029a3b38a2b98f875b84a4bd182eebd048218
|
||||
React-RCTVibration: 655e32ab1393718662b8a6f75eef9f3d2ceedf96
|
||||
React-runtimeexecutor: b529b2ffeac2f15b775a37226471a2523a3f7844
|
||||
React-TurboModuleCxx-RNW: 18bb71af41fe34c8b12a56bef60aae7ee32b0817
|
||||
React-TurboModuleCxx-WinRTPort: 78a8f1fb00ab240213f0c1937934182774ec1d8c
|
||||
ReactCommon: 733e0f7149c889701ad3992f82c1bda8b21edcb7
|
||||
Yoga: 4b69710d7e082acfb7daca42ceadd79d196a67cd
|
||||
React-TurboModuleCxx-WinRTPort: 29b59397f335c90defd1c74eaf0982ccd13c52be
|
||||
ReactCommon: 989cfe7b74513b6bed09cf75e733bc631ec963c7
|
||||
Yoga: ba2f886e24a03fa7dad871b727b5de2a7205dd8a
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 7d43a928a9b9ad27329da110adbfadd923a39ba8
|
||||
|
|
|
@ -866,6 +866,23 @@ static id RCTPropChecker(NSString *prop, NSNumber *value)
|
|||
[_uiManager verify];
|
||||
}
|
||||
|
||||
- (void) testGetValue
|
||||
{
|
||||
__block NSInteger saveValueCallbackCalls = 0;
|
||||
NSNumber *nodeTag = @100;
|
||||
[_nodesManager createAnimatedNode:nodeTag
|
||||
config:@{@"type": @"value", @"value": @1, @"offset": @0}];
|
||||
RCTResponseSenderBlock saveValueCallback = ^(NSArray *response) {
|
||||
saveValueCallbackCalls++;
|
||||
XCTAssertEqualObjects(response, @[@1]);
|
||||
};
|
||||
|
||||
XCTAssertEqual(saveValueCallbackCalls, 0);
|
||||
|
||||
[_nodesManager getValue:nodeTag saveCallback:saveValueCallback];
|
||||
XCTAssertEqual(saveValueCallbackCalls, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a following graph of nodes:
|
||||
* Value(3, initialValue) ----> Style(4) ---> Props(5) ---> View(viewTag)
|
||||
|
|
|
@ -335,6 +335,115 @@ exports.examples = [
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Moving box example',
|
||||
description: ('Click arrow buttons to move the box.' +
|
||||
'Then hide the box and reveal it again.' +
|
||||
'After that the box position will reset to initial position.': string),
|
||||
render: (): React.Node => {
|
||||
const containerWidth = 200;
|
||||
const boxSize = 50;
|
||||
|
||||
const movingBoxStyles = StyleSheet.create({
|
||||
container: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: '#fff',
|
||||
padding: 30,
|
||||
},
|
||||
boxContainer: {
|
||||
backgroundColor: '#d3d3d3',
|
||||
height: boxSize,
|
||||
width: containerWidth,
|
||||
},
|
||||
box: {
|
||||
width: boxSize,
|
||||
height: boxSize,
|
||||
margin: 0,
|
||||
},
|
||||
buttonsContainer: {
|
||||
marginTop: 20,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: containerWidth,
|
||||
},
|
||||
});
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = {|boxVisible: boolean|};
|
||||
|
||||
class MovingBoxExample extends React.Component<Props, State> {
|
||||
x: Animated.Value;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.x = new Animated.Value(0);
|
||||
this.state = {
|
||||
boxVisible: true,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {boxVisible} = this.state;
|
||||
const toggleText = boxVisible ? 'Hide' : 'Show';
|
||||
return (
|
||||
<View style={movingBoxStyles.container}>
|
||||
{this.renderBox()}
|
||||
<View style={movingBoxStyles.buttonsContainer}>
|
||||
<RNTesterButton onPress={() => this.moveTo(0)}>
|
||||
{'<-'}
|
||||
</RNTesterButton>
|
||||
<RNTesterButton onPress={this.toggleVisibility}>
|
||||
{toggleText}
|
||||
</RNTesterButton>
|
||||
<RNTesterButton
|
||||
onPress={() => this.moveTo(containerWidth - boxSize)}>
|
||||
{'->'}
|
||||
</RNTesterButton>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
renderBox = () => {
|
||||
if (this.state.boxVisible) {
|
||||
const horizontalLocation = {transform: [{translateX: this.x}]};
|
||||
return (
|
||||
<View style={movingBoxStyles.boxContainer}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.content,
|
||||
movingBoxStyles.box,
|
||||
horizontalLocation,
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<View style={movingBoxStyles.boxContainer}>
|
||||
<Text>The box view is not being rendered</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
moveTo = x => {
|
||||
Animated.timing(this.x, {
|
||||
toValue: x,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
};
|
||||
|
||||
toggleVisibility = () => {
|
||||
const {boxVisible} = this.state;
|
||||
this.setState({boxVisible: !boxVisible});
|
||||
};
|
||||
}
|
||||
return <MovingBoxExample />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Continuous Interactions',
|
||||
description: ('Gesture events, chaining, 2D ' +
|
||||
|
|
|
@ -14,10 +14,10 @@ const React = require('react');
|
|||
|
||||
const {DeviceEventEmitter, Text, View} = require('react-native');
|
||||
|
||||
import type EmitterSubscription from '../../../../Libraries/vendor/emitter/EmitterSubscription';
|
||||
import {type EventSubscription} from '../../../../Libraries/vendor/emitter/EventEmitter';
|
||||
|
||||
class OrientationChangeExample extends React.Component<{...}, $FlowFixMeState> {
|
||||
_orientationSubscription: EmitterSubscription;
|
||||
_orientationSubscription: EventSubscription;
|
||||
|
||||
state = {
|
||||
currentOrientation: '',
|
||||
|
|
|
@ -210,7 +210,10 @@ function FallbackColorsExample() {
|
|||
color: PlatformColor('bogus', '@color/catalyst_redbox_background'),
|
||||
};
|
||||
} else {
|
||||
throw 'Unexpected Platform.OS: ' + Platform.OS;
|
||||
color = {
|
||||
label: 'Unexpected Platform.OS: ' + Platform.OS,
|
||||
color: 'red',
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -312,6 +315,7 @@ function VariantColorsExample() {
|
|||
ios: "DynamicColorIOS({light: 'red', dark: 'blue'})",
|
||||
android: "PlatformColor('?attr/colorAccent')",
|
||||
macos: "DynamicColorMacOS({light: 'red', dark: 'blue'})",
|
||||
default: 'Unexpected Platform.OS: ' + Platform.OS,
|
||||
})
|
||||
// ]TODO(OSS Candidate ISS#2710739)
|
||||
}
|
||||
|
@ -326,7 +330,9 @@ function VariantColorsExample() {
|
|||
Platform.OS === 'macos'
|
||||
? DynamicColorMacOS({light: 'red', dark: 'blue'})
|
||||
: // ]TODO(macOS GH#774)
|
||||
PlatformColor('?attr/colorAccent'),
|
||||
Platform.OS === 'android'
|
||||
? PlatformColor('?attr/colorAccent')
|
||||
: 'red',
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -146,6 +146,7 @@ class AdjustingFontSize extends React.Component<
|
|||
<Text
|
||||
adjustsFontSizeToFit={true}
|
||||
numberOfLines={4}
|
||||
android_hyphenationFrequency="normal"
|
||||
style={{fontSize: 20, marginVertical: 6}}>
|
||||
{'Multiline text component shrinking is supported, watch as this reeeeaaaally loooooong teeeeeeext grooooows and then shriiiinks as you add text to me! ioahsdia soady auydoa aoisyd aosdy ' +
|
||||
' ' +
|
||||
|
@ -207,6 +208,28 @@ class TextExample extends React.Component<{...}> {
|
|||
going to the next line.
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Hyphenation">
|
||||
<Text android_hyphenationFrequency="normal">
|
||||
<Text style={{color: 'red'}}>Normal: </Text>
|
||||
WillHaveAnHyphenWhenBreakingForNewLine
|
||||
</Text>
|
||||
<Text android_hyphenationFrequency="none">
|
||||
<Text style={{color: 'red'}}>None: </Text>
|
||||
WillNotHaveAnHyphenWhenBreakingForNewLine
|
||||
</Text>
|
||||
<Text android_hyphenationFrequency="full">
|
||||
<Text style={{color: 'red'}}>Full: </Text>
|
||||
WillHaveAnHyphenWhenBreakingForNewLine
|
||||
</Text>
|
||||
<Text android_hyphenationFrequency="high">
|
||||
<Text style={{color: 'red'}}>High: </Text>
|
||||
WillHaveAnHyphenWhenBreakingForNewLine
|
||||
</Text>
|
||||
<Text android_hyphenationFrequency="balanced">
|
||||
<Text style={{color: 'red'}}>Balanced: </Text>
|
||||
WillHaveAnHyphenWhenBreakingForNewLine
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
<RNTesterBlock title="Padding">
|
||||
<Text style={{padding: 10}}>
|
||||
This text is indented by 10px padding on all sides.
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
NSHashTable<NSString *> *other = [NSHashTable new];
|
||||
|
||||
RCTLayoutContext layoutContext = {};
|
||||
layoutContext.absolutePosition = CGPointZero;
|
||||
layoutContext.affectedShadowViews = affectedShadowViews;
|
||||
layoutContext.other = other;
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTViewComponentView.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* UIView class for root <InputAccessoryView> component.
|
||||
*/
|
||||
@interface RCTInputAccessoryComponentView : RCTViewComponentView
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTInputAccessoryComponentView.h"
|
||||
|
||||
#import <React/RCTBackedTextInputViewProtocol.h>
|
||||
#import <React/RCTConversions.h>
|
||||
#import <React/RCTSurfaceTouchHandler.h>
|
||||
#import <React/UIView+React.h>
|
||||
#import <react/components/inputaccessory/InputAccessoryComponentDescriptor.h>
|
||||
#import <react/components/rncore/Props.h>
|
||||
#import "RCTInputAccessoryContentView.h"
|
||||
|
||||
#import "RCTFabricComponentsPlugins.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
static UIView<RCTBackedTextInputViewProtocol> *_Nullable RCTFindTextInputWithNativeId(UIView *view, NSString *nativeId)
|
||||
{
|
||||
if ([view respondsToSelector:@selector(inputAccessoryViewID)] &&
|
||||
[view respondsToSelector:@selector(setInputAccessoryView:)]) {
|
||||
UIView<RCTBackedTextInputViewProtocol> *typed = (UIView<RCTBackedTextInputViewProtocol> *)view;
|
||||
if (!nativeId || [typed.inputAccessoryViewID isEqualToString:nativeId]) {
|
||||
return typed;
|
||||
}
|
||||
}
|
||||
|
||||
for (UIView *subview in view.subviews) {
|
||||
UIView<RCTBackedTextInputViewProtocol> *result = RCTFindTextInputWithNativeId(subview, nativeId);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@implementation RCTInputAccessoryComponentView {
|
||||
InputAccessoryShadowNode::ConcreteState::Shared _state;
|
||||
RCTInputAccessoryContentView *_contentView;
|
||||
RCTSurfaceTouchHandler *_touchHandler;
|
||||
UIView<RCTBackedTextInputViewProtocol> __weak *_textInput;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
static const auto defaultProps = std::make_shared<const InputAccessoryProps>();
|
||||
_props = defaultProps;
|
||||
_contentView = [RCTInputAccessoryContentView new];
|
||||
_touchHandler = [RCTSurfaceTouchHandler new];
|
||||
[_touchHandler attachToView:_contentView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
[super didMoveToWindow];
|
||||
|
||||
if (self.window && !_textInput) {
|
||||
if (self.nativeId) {
|
||||
_textInput = RCTFindTextInputWithNativeId(self.window, self.nativeId);
|
||||
_textInput.inputAccessoryView = _contentView;
|
||||
} else {
|
||||
_textInput = RCTFindTextInputWithNativeId(_contentView, nil);
|
||||
}
|
||||
|
||||
if (!self.nativeId) {
|
||||
[self becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
- (UIView *)inputAccessoryView
|
||||
{
|
||||
return _contentView;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
{
|
||||
return concreteComponentDescriptorProvider<InputAccessoryComponentDescriptor>();
|
||||
}
|
||||
|
||||
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
||||
{
|
||||
[_contentView insertSubview:childComponentView atIndex:index];
|
||||
}
|
||||
|
||||
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
|
||||
{
|
||||
[childComponentView removeFromSuperview];
|
||||
}
|
||||
|
||||
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
||||
{
|
||||
auto const &oldInputAccessoryProps = *std::static_pointer_cast<InputAccessoryProps const>(_props);
|
||||
auto const &newInputAccessoryProps = *std::static_pointer_cast<InputAccessoryProps const>(props);
|
||||
|
||||
if (newInputAccessoryProps.backgroundColor != oldInputAccessoryProps.backgroundColor) {
|
||||
_contentView.backgroundColor = RCTUIColorFromSharedColor(newInputAccessoryProps.backgroundColor);
|
||||
}
|
||||
|
||||
[super updateProps:props oldProps:oldProps];
|
||||
self.hidden = true;
|
||||
}
|
||||
|
||||
- (void)updateState:(const facebook::react::State::Shared &)state
|
||||
oldState:(const facebook::react::State::Shared &)oldState
|
||||
{
|
||||
_state = std::static_pointer_cast<InputAccessoryShadowNode::ConcreteState const>(state);
|
||||
CGSize oldScreenSize = RCTCGSizeFromSize(_state->getData().screenSize);
|
||||
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
|
||||
screenSize.height = std::nan("");
|
||||
if (oldScreenSize.width != screenSize.width) {
|
||||
auto stateData = InputAccessoryState{RCTSizeFromCGSize(screenSize)};
|
||||
_state->updateState(std::move(stateData));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
|
||||
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
|
||||
{
|
||||
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
||||
|
||||
[_contentView setFrame:RCTCGRectFromRect(layoutMetrics.getContentFrame())];
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
_state.reset();
|
||||
_textInput = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class<RCTComponentViewProtocol> RCTInputAccessoryCls(void)
|
||||
{
|
||||
return RCTInputAccessoryComponentView.class;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTInputAccessoryContentView : UIView
|
||||
|
||||
@end
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTInputAccessoryContentView.h"
|
||||
|
||||
@implementation RCTInputAccessoryContentView {
|
||||
UIView *_safeAreaContainer;
|
||||
NSLayoutConstraint *_heightConstraint;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleHeight;
|
||||
|
||||
_safeAreaContainer = [UIView new];
|
||||
_safeAreaContainer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[self addSubview:_safeAreaContainer];
|
||||
|
||||
_heightConstraint = [_safeAreaContainer.heightAnchor constraintEqualToConstant:0];
|
||||
_heightConstraint.active = YES;
|
||||
|
||||
if (@available(iOS 11.0, tvOS 11.0, *)) {
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor],
|
||||
[_safeAreaContainer.topAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.topAnchor],
|
||||
[_safeAreaContainer.leadingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.leadingAnchor],
|
||||
[_safeAreaContainer.trailingAnchor constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor]
|
||||
]];
|
||||
} else {
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[_safeAreaContainer.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
|
||||
[_safeAreaContainer.topAnchor constraintEqualToAnchor:self.topAnchor],
|
||||
[_safeAreaContainer.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
|
||||
[_safeAreaContainer.trailingAnchor constraintEqualToAnchor:self.trailingAnchor]
|
||||
]];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)intrinsicContentSize
|
||||
{
|
||||
// This is needed so the view size is based on autolayout constraints.
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index
|
||||
{
|
||||
[_safeAreaContainer insertSubview:view atIndex:index];
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
[_safeAreaContainer setFrame:frame];
|
||||
_heightConstraint.constant = frame.size.height;
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@end
|
|
@ -40,6 +40,7 @@ Class<RCTComponentViewProtocol> RCTModalHostViewCls(void) __attribute__((used));
|
|||
Class<RCTComponentViewProtocol> RCTImageCls(void) __attribute__((used));
|
||||
Class<RCTComponentViewProtocol> RCTParagraphCls(void) __attribute__((used));
|
||||
Class<RCTComponentViewProtocol> RCTTextInputCls(void) __attribute__((used));
|
||||
Class<RCTComponentViewProtocol> RCTInputAccessoryCls(void) __attribute__((used));
|
||||
Class<RCTComponentViewProtocol> RCTViewCls(void) __attribute__((used));
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -29,6 +29,7 @@ Class<RCTComponentViewProtocol> RCTFabricComponentsProvider(const char *name) {
|
|||
{"Image", RCTImageCls},
|
||||
{"Paragraph", RCTParagraphCls},
|
||||
{"TextInput", RCTTextInputCls},
|
||||
{"InputAccessoryView", RCTInputAccessoryCls},
|
||||
{"View", RCTViewCls},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ using namespace facebook::react;
|
|||
BOOL _didMoveToWindow;
|
||||
}
|
||||
|
||||
#pragma mark - UIView overrides
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
|
@ -89,6 +91,13 @@ using namespace facebook::react;
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTViewComponentView overrides
|
||||
|
||||
- (NSObject *)accessibilityElement
|
||||
{
|
||||
return _backedTextInputView;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
||||
|
@ -198,6 +207,10 @@ using namespace facebook::react;
|
|||
_backedTextInputView.tintColor = RCTUIColorFromSharedColor(newTextInputProps.selectionColor);
|
||||
}
|
||||
|
||||
if (newTextInputProps.inputAccessoryViewID != oldTextInputProps.inputAccessoryViewID) {
|
||||
_backedTextInputView.inputAccessoryViewID = RCTNSStringFromString(newTextInputProps.inputAccessoryViewID);
|
||||
}
|
||||
|
||||
[super updateProps:props oldProps:oldProps];
|
||||
}
|
||||
|
||||
|
@ -230,20 +243,6 @@ using namespace facebook::react;
|
|||
RCTUIEdgeInsetsFromEdgeInsets(layoutMetrics.contentInsets - layoutMetrics.borderWidth);
|
||||
}
|
||||
|
||||
- (void)_setAttributedString:(NSAttributedString *)attributedString
|
||||
{
|
||||
UITextRange *selectedRange = [_backedTextInputView selectedTextRange];
|
||||
_backedTextInputView.attributedText = attributedString;
|
||||
if (_lastStringStateWasUpdatedWith.length == attributedString.length) {
|
||||
// Calling `[_backedTextInputView setAttributedText]` moves caret
|
||||
// to the end of text input field. This cancels any selection as well
|
||||
// as position in the text input field. In case the length of string
|
||||
// doesn't change, selection and caret position is maintained.
|
||||
[_backedTextInputView setSelectedTextRange:selectedRange notifyDelegate:NO];
|
||||
}
|
||||
_lastStringStateWasUpdatedWith = attributedString;
|
||||
}
|
||||
|
||||
- (void)prepareForRecycle
|
||||
{
|
||||
[super prepareForRecycle];
|
||||
|
@ -256,19 +255,6 @@ using namespace facebook::react;
|
|||
_didMoveToWindow = NO;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponentViewProtocol
|
||||
|
||||
- (void)_setMultiline:(BOOL)multiline
|
||||
{
|
||||
[_backedTextInputView removeFromSuperview];
|
||||
UIView<RCTBackedTextInputViewProtocol> *backedTextInputView =
|
||||
multiline ? [[RCTUITextView alloc] init] : [[RCTUITextField alloc] init];
|
||||
backedTextInputView.frame = _backedTextInputView.frame;
|
||||
RCTCopyBackedTextInput(_backedTextInputView, backedTextInputView);
|
||||
_backedTextInputView = backedTextInputView;
|
||||
[self addSubview:_backedTextInputView];
|
||||
}
|
||||
|
||||
#pragma mark - RCTBackedTextInputDelegate
|
||||
|
||||
- (BOOL)textInputShouldBeginEditing
|
||||
|
@ -393,41 +379,6 @@ using namespace facebook::react;
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Other
|
||||
|
||||
- (TextInputMetrics)_textInputMetrics
|
||||
{
|
||||
TextInputMetrics metrics;
|
||||
metrics.text = RCTStringFromNSString(_backedTextInputView.attributedText.string);
|
||||
metrics.selectionRange = [self _selectionRange];
|
||||
metrics.eventCount = _mostRecentEventCount;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
- (void)_updateState
|
||||
{
|
||||
if (!_state) {
|
||||
return;
|
||||
}
|
||||
NSAttributedString *attributedString = _backedTextInputView.attributedText;
|
||||
auto data = _state->getData();
|
||||
_lastStringStateWasUpdatedWith = attributedString;
|
||||
data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString);
|
||||
_mostRecentEventCount += _comingFromJS ? 0 : 1;
|
||||
data.mostRecentEventCount = _mostRecentEventCount;
|
||||
_state->updateState(std::move(data));
|
||||
}
|
||||
|
||||
- (AttributedString::Range)_selectionRange
|
||||
{
|
||||
UITextRange *selectedTextRange = _backedTextInputView.selectedTextRange;
|
||||
NSInteger start = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
||||
toPosition:selectedTextRange.start];
|
||||
NSInteger end = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
||||
toPosition:selectedTextRange.end];
|
||||
return AttributedString::Range{(int)start, (int)(end - start)};
|
||||
}
|
||||
|
||||
#pragma mark - Native Commands
|
||||
|
||||
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
||||
|
@ -475,6 +426,66 @@ using namespace facebook::react;
|
|||
_comingFromJS = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Other
|
||||
|
||||
- (TextInputMetrics)_textInputMetrics
|
||||
{
|
||||
TextInputMetrics metrics;
|
||||
metrics.text = RCTStringFromNSString(_backedTextInputView.attributedText.string);
|
||||
metrics.selectionRange = [self _selectionRange];
|
||||
metrics.eventCount = _mostRecentEventCount;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
- (void)_updateState
|
||||
{
|
||||
if (!_state) {
|
||||
return;
|
||||
}
|
||||
NSAttributedString *attributedString = _backedTextInputView.attributedText;
|
||||
auto data = _state->getData();
|
||||
_lastStringStateWasUpdatedWith = attributedString;
|
||||
data.attributedStringBox = RCTAttributedStringBoxFromNSAttributedString(attributedString);
|
||||
_mostRecentEventCount += _comingFromJS ? 0 : 1;
|
||||
data.mostRecentEventCount = _mostRecentEventCount;
|
||||
_state->updateState(std::move(data));
|
||||
}
|
||||
|
||||
- (AttributedString::Range)_selectionRange
|
||||
{
|
||||
UITextRange *selectedTextRange = _backedTextInputView.selectedTextRange;
|
||||
NSInteger start = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
||||
toPosition:selectedTextRange.start];
|
||||
NSInteger end = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
|
||||
toPosition:selectedTextRange.end];
|
||||
return AttributedString::Range{(int)start, (int)(end - start)};
|
||||
}
|
||||
|
||||
- (void)_setAttributedString:(NSAttributedString *)attributedString
|
||||
{
|
||||
UITextRange *selectedRange = [_backedTextInputView selectedTextRange];
|
||||
_backedTextInputView.attributedText = attributedString;
|
||||
if (_lastStringStateWasUpdatedWith.length == attributedString.length) {
|
||||
// Calling `[_backedTextInputView setAttributedText]` moves caret
|
||||
// to the end of text input field. This cancels any selection as well
|
||||
// as position in the text input field. In case the length of string
|
||||
// doesn't change, selection and caret position is maintained.
|
||||
[_backedTextInputView setSelectedTextRange:selectedRange notifyDelegate:NO];
|
||||
}
|
||||
_lastStringStateWasUpdatedWith = attributedString;
|
||||
}
|
||||
|
||||
- (void)_setMultiline:(BOOL)multiline
|
||||
{
|
||||
[_backedTextInputView removeFromSuperview];
|
||||
UIView<RCTBackedTextInputViewProtocol> *backedTextInputView =
|
||||
multiline ? [[RCTUITextView alloc] init] : [[RCTUITextField alloc] init];
|
||||
backedTextInputView.frame = _backedTextInputView.frame;
|
||||
RCTCopyBackedTextInput(_backedTextInputView, backedTextInputView);
|
||||
_backedTextInputView = backedTextInputView;
|
||||
[self addSubview:_backedTextInputView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class<RCTComponentViewProtocol> RCTTextInputCls(void)
|
||||
|
|
|
@ -53,18 +53,6 @@ using namespace facebook::react;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
// Consider whether using `updateLayoutMetrics:oldLayoutMetrics`
|
||||
// isn't more appropriate for your use case. `layoutSubviews` is called
|
||||
// by UIKit while `updateLayoutMetrics:oldLayoutMetrics` is called
|
||||
// by React Native Renderer within `CATransaction`.
|
||||
// If you are calling `setFrame:` or other methods that cause
|
||||
// `layoutSubviews` to be triggered, `_contentView`'s and `_borderLayout`'s
|
||||
// frames might get out of sync with `self.bounds`.
|
||||
}
|
||||
|
||||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
|
||||
|
|
|
@ -18,60 +18,6 @@
|
|||
|
||||
using namespace facebook::react;
|
||||
|
||||
#define LEGACY_UIMANAGER_INTEGRATION_ENABLED 1
|
||||
|
||||
#ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
|
||||
|
||||
#import <React/RCTBridge+Private.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
|
||||
/**
|
||||
* Warning: This is a total hack and temporary solution.
|
||||
* Unless we have a pure Fabric-based implementation of UIManager commands
|
||||
* delivery pipeline, we have to leverage existing infra. This code tricks
|
||||
* legacy UIManager by registering all Fabric-managed views in it,
|
||||
* hence existing command-delivery infra can reach "foreign" views using
|
||||
* the old pipeline.
|
||||
*/
|
||||
@interface RCTUIManager ()
|
||||
- (NSMutableDictionary<NSNumber *, UIView *> *)viewRegistry;
|
||||
@end
|
||||
|
||||
@interface RCTUIManager (Hack)
|
||||
|
||||
+ (void)registerView:(UIView *)view;
|
||||
+ (void)unregisterView:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUIManager (Hack)
|
||||
|
||||
+ (void)registerView:(UIView *)view
|
||||
{
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager];
|
||||
view.reactTag = @(view.tag);
|
||||
[uiManager.viewRegistry setObject:view forKey:@(view.tag)];
|
||||
}
|
||||
|
||||
+ (void)unregisterView:(UIView *)view
|
||||
{
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager];
|
||||
view.reactTag = nil;
|
||||
[uiManager.viewRegistry removeObjectForKey:@(view.tag)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
|
||||
|
||||
@implementation RCTComponentViewRegistry {
|
||||
|
@ -133,10 +79,6 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
|
|||
|
||||
_registry.insert({tag, componentViewDescriptor});
|
||||
|
||||
#ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
|
||||
[RCTUIManager registerView:componentViewDescriptor.view];
|
||||
#endif
|
||||
|
||||
return componentViewDescriptor;
|
||||
}
|
||||
|
||||
|
@ -149,10 +91,6 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
|
|||
RCTAssert(
|
||||
_registry.find(tag) != _registry.end(), @"RCTComponentViewRegistry: Attempt to enqueue unregistered component.");
|
||||
|
||||
#ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
|
||||
[RCTUIManager unregisterView:componentViewDescriptor.view];
|
||||
#endif
|
||||
|
||||
_registry.erase(tag);
|
||||
componentViewDescriptor.view.tag = 0;
|
||||
[self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor];
|
||||
|
|
|
@ -74,6 +74,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)removeObserver:(id<RCTSurfacePresenterObserver>)observer;
|
||||
|
||||
/*
|
||||
* Please do not use this, this will be deleted soon.
|
||||
*/
|
||||
- (nullable UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -191,6 +191,13 @@ static inline LayoutContext RCTGetLayoutContext()
|
|||
surfaceId:surface.rootTag];
|
||||
}
|
||||
|
||||
- (UIView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag
|
||||
{
|
||||
UIView<RCTComponentViewProtocol> *componentView =
|
||||
[_mountingManager.componentViewRegistry findComponentViewWithTag:tag];
|
||||
return componentView;
|
||||
}
|
||||
|
||||
- (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props
|
||||
{
|
||||
RCTScheduler *scheduler = [self _scheduler];
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#import <objc/runtime.h>
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTUIKit.h> // TODO(macOS GH#774)
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -26,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@protocol RCTSurfacePresenterStub <NSObject>
|
||||
|
||||
- (nullable RCTPlatformView *)findComponentViewWithTag_DO_NOT_USE_DEPRECATED:(NSInteger)tag; // TODO(macOS GH#774)
|
||||
- (BOOL)synchronouslyUpdateViewOnUIThread:(NSNumber *)reactTag props:(NSDictionary *)props;
|
||||
- (void)addObserver:(id<RCTSurfacePresenterObserver>)observer;
|
||||
- (void)removeObserver:(id<RCTSurfacePresenterObserver>)observer;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#import "RCTUIManager.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <React/RCTSurfacePresenterStub.h>
|
||||
|
||||
#import <yoga/Yoga.h> // TODO(macOS GH#774)
|
||||
|
||||
|
@ -369,7 +370,11 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
- (RCTPlatformView *)viewForReactTag:(NSNumber *)reactTag // TODO(macOS GH#774)
|
||||
{
|
||||
RCTAssertMainQueue();
|
||||
return _viewRegistry[reactTag];
|
||||
RCTPlatformView *view = [_bridge.surfacePresenter findComponentViewWithTag_DO_NOT_USE_DEPRECATED:reactTag.integerValue]; // TODO(macOS GH#774)
|
||||
if (!view) {
|
||||
view = _viewRegistry[reactTag];
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowViewForReactTag:(NSNumber *)reactTag
|
||||
|
@ -1475,7 +1480,8 @@ RCT_EXPORT_METHOD(setJSResponder
|
|||
{
|
||||
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTPlatformView *> *viewRegistry) { // TODO(macOS GH#774)
|
||||
_jsResponder = viewRegistry[reactTag];
|
||||
if (!_jsResponder) {
|
||||
// Fabric view's are not stored in viewRegistry. We avoid logging a warning in that case.
|
||||
if (!_jsResponder && !RCTUIManagerTypeForTagIsFabric(reactTag)) {
|
||||
RCTLogWarn(@"Invalid view set to be the JS responder - tag %@", reactTag);
|
||||
}
|
||||
}];
|
||||
|
|
|
@ -66,6 +66,9 @@ public abstract class NativeAnimatedModuleSpec extends ReactContextBaseJavaModul
|
|||
public abstract void startAnimatingNode(double animationId, double nodeTag, ReadableMap config,
|
||||
Callback endCallback);
|
||||
|
||||
@ReactMethod
|
||||
public abstract void getValue(double tag, Callback saveValueCallback);
|
||||
|
||||
@ReactMethod
|
||||
public abstract void stopListeningToAnimatedNodeValue(double tag);
|
||||
|
||||
|
|
|
@ -186,6 +186,10 @@ namespace facebook {
|
|||
return static_cast<JavaTurboModule&>(turboModule).invokeJavaMethod(rt, VoidKind, "createAnimatedNode", "(DLcom/facebook/react/bridge/ReadableMap;)V", args, count);
|
||||
}
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<JavaTurboModule&>(turboModule).invokeJavaMethod(rt, VoidKind, "getValue", "(DLcom/facebook/react/bridge/Callback;)V", args, count);
|
||||
}
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<JavaTurboModule&>(turboModule).invokeJavaMethod(rt, VoidKind, "startListeningToAnimatedNodeValue", "(D)V", args, count);
|
||||
}
|
||||
|
@ -265,6 +269,9 @@ namespace facebook {
|
|||
methodMap_["createAnimatedNode"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_createAnimatedNode};
|
||||
|
||||
|
||||
methodMap_["getValue"] = MethodMetadata {2, __hostFunction_NativeAnimatedModuleSpecJSI_getValue};
|
||||
|
||||
|
||||
methodMap_["startListeningToAnimatedNodeValue"] = MethodMetadata {1, __hostFunction_NativeAnimatedModuleSpecJSI_startListeningToAnimatedNodeValue};
|
||||
|
||||
|
||||
|
|
|
@ -774,4 +774,16 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
|||
public void removeListeners(double count) {
|
||||
// iOS only
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getValue(final double animatedValueNodeTagDouble, final Callback callback) {
|
||||
final int animatedValueNodeTag = (int) animatedValueNodeTagDouble;
|
||||
mOperations.add(
|
||||
new UIThreadOperation() {
|
||||
@Override
|
||||
public void execute(NativeAnimatedNodesManager animatedNodesManager) {
|
||||
animatedNodesManager.getValue(animatedValueNodeTag, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -352,6 +352,15 @@ import java.util.Queue;
|
|||
propsAnimatedNode.disconnectFromView(viewTag);
|
||||
}
|
||||
|
||||
public void getValue(int tag, Callback callback) {
|
||||
AnimatedNode node = mAnimatedNodes.get(tag);
|
||||
if (node == null || !(node instanceof ValueAnimatedNode)) {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Animated node with tag " + tag + " does not exists or is not a 'value' node");
|
||||
}
|
||||
callback.invoke(((ValueAnimatedNode) node).getValue());
|
||||
}
|
||||
|
||||
public void restoreDefaultValues(int animatedNodeTag) {
|
||||
AnimatedNode node = mAnimatedNodes.get(animatedNodeTag);
|
||||
// Restoring default values needs to happen before UIManager operations so it is
|
||||
|
|
|
@ -340,16 +340,6 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
|||
FLog.d(ReactConstants.TAG, "CatalystInstanceImpl.destroy() start");
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (ReactFeatureFlags.useCatalystTeardownV2) {
|
||||
destroyV2();
|
||||
} else {
|
||||
destroyV1();
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadConfined(UI)
|
||||
public void destroyV1() {
|
||||
FLog.d(ReactConstants.TAG, "CatalystInstanceImpl.destroyV1() start");
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (mDestroyed) {
|
||||
|
@ -427,101 +417,6 @@ public class CatalystInstanceImpl implements CatalystInstance {
|
|||
Systrace.unregisterListener(mTraceListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys this catalyst instance, waiting for any other threads in ReactQueueConfiguration
|
||||
* (besides the UI thread) to finish running. Must be called from the UI thread so that we can
|
||||
* fully shut down other threads.
|
||||
*/
|
||||
@ThreadConfined(UI)
|
||||
public void destroyV2() {
|
||||
FLog.d(ReactConstants.TAG, "CatalystInstanceImpl.destroyV2() start");
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: tell all APIs to shut down
|
||||
ReactMarker.logMarker(ReactMarkerConstants.DESTROY_CATALYST_INSTANCE_START);
|
||||
mDestroyed = true;
|
||||
mNativeModulesThreadDestructionComplete = false;
|
||||
mJSThreadDestructionComplete = false;
|
||||
|
||||
mNativeModulesQueueThread.runOnQueue(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FLog.d("CatalystInstanceImpl", ".destroy on native modules thread");
|
||||
mNativeModuleRegistry.notifyJSInstanceDestroy();
|
||||
|
||||
// Notifies all JSI modules that they are being destroyed, including the FabricUIManager
|
||||
// and Fabric Scheduler
|
||||
mJSIModuleRegistry.notifyJSInstanceDestroy();
|
||||
boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0);
|
||||
if (!mBridgeIdleListeners.isEmpty()) {
|
||||
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
|
||||
if (!wasIdle) {
|
||||
listener.onTransitionToBridgeIdle();
|
||||
}
|
||||
listener.onBridgeDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
mNativeModulesThreadDestructionComplete = true;
|
||||
FLog.d("CatalystInstanceImpl", ".destroy on native modules thread finished");
|
||||
}
|
||||
});
|
||||
|
||||
getReactQueueConfiguration()
|
||||
.getJSQueueThread()
|
||||
.runOnQueue(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FLog.d("CatalystInstanceImpl", ".destroy on JS thread");
|
||||
// We need to destroy the TurboModuleManager on the JS Thread
|
||||
if (mTurboModuleManagerJSIModule != null) {
|
||||
mTurboModuleManagerJSIModule.onCatalystInstanceDestroy();
|
||||
}
|
||||
|
||||
mJSThreadDestructionComplete = true;
|
||||
FLog.d("CatalystInstanceImpl", ".destroy on JS thread finished");
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until destruction is complete
|
||||
long waitStartTime = System.currentTimeMillis();
|
||||
while (!mNativeModulesThreadDestructionComplete || !mJSThreadDestructionComplete) {
|
||||
// Never wait here, blocking the UI thread, for more than 100ms
|
||||
if ((System.currentTimeMillis() - waitStartTime) > 100) {
|
||||
FLog.w(
|
||||
ReactConstants.TAG,
|
||||
"CatalystInstanceImpl.destroy() timed out waiting for Native Modules and JS thread teardown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Kill non-UI threads from neutral third party
|
||||
// potentially expensive, so don't run on UI thread
|
||||
|
||||
// contextHolder is used as a lock to guard against
|
||||
// other users of the JS VM having the VM destroyed
|
||||
// underneath them, so notify them before we reset
|
||||
// Native
|
||||
mJavaScriptContextHolder.clear();
|
||||
|
||||
// Imperatively destruct the C++ CatalystInstance rather than
|
||||
// wait for the JVM's GC to free it.
|
||||
mHybridData.resetNative();
|
||||
|
||||
getReactQueueConfiguration().destroy();
|
||||
FLog.d(ReactConstants.TAG, "CatalystInstanceImpl.destroy() end");
|
||||
ReactMarker.logMarker(ReactMarkerConstants.DESTROY_CATALYST_INSTANCE_END);
|
||||
|
||||
// This is a noop if the listener was not yet registered.
|
||||
Systrace.unregisterListener(mTraceListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return mDestroyed;
|
||||
|
|
|
@ -296,9 +296,6 @@ public class ReactContext extends ContextWrapper {
|
|||
mDestroyed = true;
|
||||
if (mCatalystInstance != null) {
|
||||
mCatalystInstance.destroy();
|
||||
if (ReactFeatureFlags.nullifyCatalystInstanceOnDestroy) {
|
||||
mCatalystInstance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,28 +41,7 @@ public class ReactFeatureFlags {
|
|||
* inside view manager will be called instead.
|
||||
*/
|
||||
public static boolean useViewManagerDelegatesForCommands = false;
|
||||
|
||||
/**
|
||||
* Should this application use Catalyst Teardown V2? This is an experiment to use a V2 of the
|
||||
* CatalystInstanceImpl `destroy` method.
|
||||
*/
|
||||
public static boolean useCatalystTeardownV2 = false;
|
||||
|
||||
/**
|
||||
* When the ReactContext is destroyed, should the CatalystInstance immediately be nullified? This
|
||||
* is the safest thing to do since the CatalystInstance shouldn't be used, and should be
|
||||
* garbage-collected after it's destroyed, but this is a breaking change in that many native
|
||||
* modules assume that a ReactContext will always have a CatalystInstance. This will be deleted
|
||||
* and the CatalystInstance will always be destroyed in some future release.
|
||||
*/
|
||||
public static boolean nullifyCatalystInstanceOnDestroy = false;
|
||||
|
||||
/**
|
||||
* Temporary flag. See UIImplementation: if this flag is enabled, ViewCommands will be queued and
|
||||
* executed before any other types of UI operations.
|
||||
*/
|
||||
public static boolean allowEarlyViewCommandExecution = false;
|
||||
|
||||
|
||||
/**
|
||||
* This react flag enables a custom algorithm for the getChildVisibleRect() method in the classes
|
||||
* ReactViewGroup, ReactHorizontalScrollView and ReactScrollView.
|
||||
|
@ -87,9 +66,6 @@ public class ReactFeatureFlags {
|
|||
/** Feature flag to configure initialization of Fabric surfaces. */
|
||||
public static boolean enableFabricStartSurfaceWithLayoutMetrics = true;
|
||||
|
||||
/** Feature flag to have FabricUIManager teardown stop all active surfaces. */
|
||||
public static boolean enableFabricStopAllSurfacesOnTeardown = false;
|
||||
|
||||
/** Feature flag to use stopSurface when ReactRootView is unmounted. */
|
||||
public static boolean enableStopSurfaceOnRootViewUnmount = false;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import com.facebook.react.fabric.mounting.LayoutMetricsConversions;
|
|||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
|
||||
|
@ -29,7 +28,6 @@ import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
|
|||
import com.facebook.react.fabric.mounting.mountitems.MountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.RemoveMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent;
|
||||
import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem;
|
||||
|
@ -112,7 +110,6 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
|
|||
FabricEventEmitter.class.getClass();
|
||||
BatchMountItem.class.getClass();
|
||||
CreateMountItem.class.getClass();
|
||||
DeleteMountItem.class.getClass();
|
||||
DispatchCommandMountItem.class.getClass();
|
||||
DispatchIntCommandMountItem.class.getClass();
|
||||
DispatchStringCommandMountItem.class.getClass();
|
||||
|
@ -120,7 +117,6 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
|
|||
MountItem.class.getClass();
|
||||
PreAllocateViewMountItem.class.getClass();
|
||||
RemoveDeleteMultiMountItem.class.getClass();
|
||||
RemoveMountItem.class.getClass();
|
||||
SendAccessibilityEvent.class.getClass();
|
||||
UpdateEventEmitterMountItem.class.getClass();
|
||||
UpdateLayoutMountItem.class.getClass();
|
||||
|
|
|
@ -56,7 +56,6 @@ import com.facebook.react.fabric.events.FabricEventEmitter;
|
|||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.fabric.mounting.mountitems.BatchMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.CreateMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DeleteMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchIntCommandMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.DispatchStringCommandMountItem;
|
||||
|
@ -64,7 +63,6 @@ import com.facebook.react.fabric.mounting.mountitems.InsertMountItem;
|
|||
import com.facebook.react.fabric.mounting.mountitems.MountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.PreAllocateViewMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.RemoveDeleteMultiMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.RemoveMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.SendAccessibilityEvent;
|
||||
import com.facebook.react.fabric.mounting.mountitems.UpdateEventEmitterMountItem;
|
||||
import com.facebook.react.fabric.mounting.mountitems.UpdateLayoutMountItem;
|
||||
|
@ -255,8 +253,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@ThreadConfined(ANY)
|
||||
@Override
|
||||
public void stopSurface(int surfaceID) {
|
||||
mBinding.stopSurface(surfaceID);
|
||||
mReactContextForRootTag.remove(surfaceID);
|
||||
mBinding.stopSurface(surfaceID);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -299,15 +297,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
// memory immediately.
|
||||
mDispatchUIFrameCallback.stop();
|
||||
|
||||
// Stop all attached surfaces
|
||||
if (ReactFeatureFlags.enableFabricStopAllSurfacesOnTeardown) {
|
||||
FLog.e(TAG, "stop all attached surfaces");
|
||||
for (int surfaceId : mReactContextForRootTag.keySet()) {
|
||||
FLog.e(TAG, "stop attached surface: " + surfaceId);
|
||||
stopSurface(surfaceId);
|
||||
}
|
||||
}
|
||||
|
||||
mBinding.unregister();
|
||||
mBinding = null;
|
||||
|
||||
|
@ -367,14 +356,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
isLayoutable);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
private MountItem removeMountItem(int reactTag, int parentReactTag, int index) {
|
||||
return new RemoveMountItem(reactTag, parentReactTag, index);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
|
@ -383,14 +364,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
return new InsertMountItem(reactTag, parentReactTag, index);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
private MountItem deleteMountItem(int reactTag) {
|
||||
return new DeleteMountItem(reactTag);
|
||||
}
|
||||
|
||||
@DoNotStrip
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
|
@ -701,10 +674,6 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@UiThread
|
||||
@ThreadConfined(UI)
|
||||
private List<DispatchCommandMountItem> getAndResetViewCommandMountItems() {
|
||||
if (!ReactFeatureFlags.allowEarlyViewCommandExecution) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
List<DispatchCommandMountItem> result = mViewCommandMountItems;
|
||||
if (result.isEmpty()) {
|
||||
|
@ -884,33 +853,12 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
BatchMountItem batchMountItem = (BatchMountItem) mountItem;
|
||||
if (!surfaceActiveForExecution(
|
||||
batchMountItem.getRootTag(), "dispatchMountItems BatchMountItem")) {
|
||||
batchMountItem.executeDeletes(mMountingManager);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: if early ViewCommand dispatch ships 100% as a feature, this can be removed.
|
||||
// This try/catch catches Retryable errors that can only be thrown by ViewCommands, which
|
||||
// won't be executed here in Early Dispatch mode.
|
||||
try {
|
||||
mountItem.execute(mMountingManager);
|
||||
} catch (RetryableMountingLayerException e) {
|
||||
// It's very common for commands to be executed on views that no longer exist - for
|
||||
// example, a blur event on TextInput being fired because of a navigation event away
|
||||
// from the current screen. If the exception is marked as Retryable, we log a soft
|
||||
// exception but never crash in debug.
|
||||
// It's not clear that logging this is even useful, because these events are very
|
||||
// common, mundane, and there's not much we can do about them currently.
|
||||
if (mountItem instanceof DispatchCommandMountItem) {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new ReactNoCrashSoftException(
|
||||
"Caught exception executing retryable mounting layer instruction: "
|
||||
+ mountItem.toString(),
|
||||
e));
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
mountItem.execute(mMountingManager);
|
||||
}
|
||||
mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime;
|
||||
}
|
||||
|
@ -1045,14 +993,8 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
|||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
private void dispatchCommandMountItem(DispatchCommandMountItem command) {
|
||||
if (ReactFeatureFlags.allowEarlyViewCommandExecution) {
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
mViewCommandMountItems.add(command);
|
||||
}
|
||||
} else {
|
||||
synchronized (mMountItemsLock) {
|
||||
mMountItems.add(command);
|
||||
}
|
||||
synchronized (mViewCommandMountItemsLock) {
|
||||
mViewCommandMountItems.add(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -280,8 +280,6 @@ void Binding::installFabricUIManager(
|
|||
|
||||
// Keep reference to config object and cache some feature flags here
|
||||
reactNativeConfig_ = config;
|
||||
shouldCollateRemovesAndDeletes_ = reactNativeConfig_->getBool(
|
||||
"react_fabric:enable_removedelete_collation_android");
|
||||
collapseDeleteCreateMountingInstructions_ = reactNativeConfig_->getBool(
|
||||
"react_fabric:enabled_collapse_delete_create_mounting_instructions");
|
||||
|
||||
|
@ -491,31 +489,6 @@ local_ref<JMountItem::javaobject> createUpdateStateMountItem(
|
|||
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr));
|
||||
}
|
||||
|
||||
local_ref<JMountItem::javaobject> createRemoveMountItem(
|
||||
const jni::global_ref<jobject> &javaUIManager,
|
||||
const ShadowViewMutation &mutation) {
|
||||
static auto removeInstruction =
|
||||
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
|
||||
->getMethod<alias_ref<JMountItem>(jint, jint, jint)>(
|
||||
"removeMountItem");
|
||||
|
||||
return removeInstruction(
|
||||
javaUIManager,
|
||||
mutation.oldChildShadowView.tag,
|
||||
mutation.parentShadowView.tag,
|
||||
mutation.index);
|
||||
}
|
||||
|
||||
local_ref<JMountItem::javaobject> createDeleteMountItem(
|
||||
const jni::global_ref<jobject> &javaUIManager,
|
||||
const ShadowViewMutation &mutation) {
|
||||
static auto deleteInstruction =
|
||||
jni::findClassStatic(Binding::UIManagerJavaDescriptor)
|
||||
->getMethod<alias_ref<JMountItem>(jint)>("deleteMountItem");
|
||||
|
||||
return deleteInstruction(javaUIManager, mutation.oldChildShadowView.tag);
|
||||
}
|
||||
|
||||
local_ref<JMountItem::javaobject> createRemoveAndDeleteMultiMountItem(
|
||||
const jni::global_ref<jobject> &javaUIManager,
|
||||
const std::vector<RemoveDeleteMetadata> &metadata) {
|
||||
|
@ -685,8 +658,7 @@ void Binding::schedulerDidFinishTransaction(
|
|||
oldChildShadowView.layoutMetrics == EmptyLayoutMetrics;
|
||||
|
||||
// Handle accumulated removals/deletions
|
||||
if (shouldCollateRemovesAndDeletes_ &&
|
||||
mutation.type != ShadowViewMutation::Remove &&
|
||||
if (mutation.type != ShadowViewMutation::Remove &&
|
||||
mutation.type != ShadowViewMutation::Delete) {
|
||||
if (toRemove.size() > 0) {
|
||||
mountItems[position++] =
|
||||
|
@ -708,39 +680,29 @@ void Binding::schedulerDidFinishTransaction(
|
|||
}
|
||||
case ShadowViewMutation::Remove: {
|
||||
if (!isVirtual) {
|
||||
if (shouldCollateRemovesAndDeletes_) {
|
||||
toRemove.push_back(
|
||||
RemoveDeleteMetadata{mutation.oldChildShadowView.tag,
|
||||
mutation.parentShadowView.tag,
|
||||
mutation.index,
|
||||
true,
|
||||
false});
|
||||
} else {
|
||||
mountItems[position++] =
|
||||
createRemoveMountItem(localJavaUIManager, mutation);
|
||||
}
|
||||
toRemove.push_back(
|
||||
RemoveDeleteMetadata{mutation.oldChildShadowView.tag,
|
||||
mutation.parentShadowView.tag,
|
||||
mutation.index,
|
||||
true,
|
||||
false});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ShadowViewMutation::Delete: {
|
||||
if (shouldCollateRemovesAndDeletes_) {
|
||||
// It is impossible to delete without removing node first
|
||||
const auto &it = std::find_if(
|
||||
std::begin(toRemove),
|
||||
std::end(toRemove),
|
||||
[&mutation](const auto &x) {
|
||||
return x.tag == mutation.oldChildShadowView.tag;
|
||||
});
|
||||
// It is impossible to delete without removing node first
|
||||
const auto &it = std::find_if(
|
||||
std::begin(toRemove),
|
||||
std::end(toRemove),
|
||||
[&mutation](const auto &x) {
|
||||
return x.tag == mutation.oldChildShadowView.tag;
|
||||
});
|
||||
|
||||
if (it != std::end(toRemove)) {
|
||||
it->shouldDelete = true;
|
||||
} else {
|
||||
toRemove.push_back(RemoveDeleteMetadata{
|
||||
mutation.oldChildShadowView.tag, -1, -1, false, true});
|
||||
}
|
||||
if (it != std::end(toRemove)) {
|
||||
it->shouldDelete = true;
|
||||
} else {
|
||||
mountItems[position++] =
|
||||
createDeleteMountItem(localJavaUIManager, mutation);
|
||||
toRemove.push_back(RemoveDeleteMetadata{
|
||||
mutation.oldChildShadowView.tag, -1, -1, false, true});
|
||||
}
|
||||
|
||||
deletedViewTags.insert(mutation.oldChildShadowView.tag);
|
||||
|
@ -840,7 +802,7 @@ void Binding::schedulerDidFinishTransaction(
|
|||
}
|
||||
|
||||
// Handle remaining removals and deletions
|
||||
if (shouldCollateRemovesAndDeletes_ && toRemove.size() > 0) {
|
||||
if (toRemove.size() > 0) {
|
||||
mountItems[position++] =
|
||||
createRemoveAndDeleteMultiMountItem(localJavaUIManager, toRemove);
|
||||
toRemove.clear();
|
||||
|
|
|
@ -125,7 +125,6 @@ class Binding : public jni::HybridClass<Binding>,
|
|||
float pointScaleFactor_ = 1;
|
||||
|
||||
std::shared_ptr<const ReactNativeConfig> reactNativeConfig_{nullptr};
|
||||
bool shouldCollateRemovesAndDeletes_{false};
|
||||
bool collapseDeleteCreateMountingInstructions_{false};
|
||||
bool disablePreallocateViews_{false};
|
||||
bool disableVirtualNodePreallocation_{false};
|
||||
|
|
|
@ -281,19 +281,17 @@ public class MountingManager {
|
|||
ViewGroupManager<ViewGroup> viewGroupManager = getViewGroupManager(viewState);
|
||||
|
||||
// Verify that the view we're about to remove has the same tag we expect
|
||||
if (tag != -1) {
|
||||
View view = viewGroupManager.getChildAt(parentView, index);
|
||||
if (view != null && view.getId() != tag) {
|
||||
throw new IllegalStateException(
|
||||
"Tried to delete view ["
|
||||
+ tag
|
||||
+ "] of parent ["
|
||||
+ parentTag
|
||||
+ "] at index "
|
||||
+ index
|
||||
+ ", but got view tag "
|
||||
+ view.getId());
|
||||
}
|
||||
View view = viewGroupManager.getChildAt(parentView, index);
|
||||
if (view != null && view.getId() != tag) {
|
||||
throw new IllegalStateException(
|
||||
"Tried to delete view ["
|
||||
+ tag
|
||||
+ "] of parent ["
|
||||
+ parentTag
|
||||
+ "] at index "
|
||||
+ index
|
||||
+ ", but got view tag "
|
||||
+ view.getId());
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -457,15 +455,6 @@ public class MountingManager {
|
|||
View view = viewState.mView;
|
||||
|
||||
if (view != null) {
|
||||
ViewParent parentView = view.getParent();
|
||||
|
||||
if (parentView != null) {
|
||||
ReactSoftException.logSoftException(
|
||||
TAG,
|
||||
new IllegalStateException(
|
||||
"Warning: Deleting view that is still attached to parent: [" + reactTag + "]"));
|
||||
}
|
||||
|
||||
dropView(view);
|
||||
} else {
|
||||
mTagToViewState.remove(reactTag);
|
||||
|
|
|
@ -45,21 +45,18 @@ public class BatchMountItem implements MountItem {
|
|||
mCommitNumber = commitNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
private void beginMarkers(String reason) {
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "FabricUIManager::mountViews - " + mSize + " items");
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"FabricUIManager::" + reason + " - " + mSize + " items");
|
||||
|
||||
if (mCommitNumber > 0) {
|
||||
ReactMarker.logFabricMarker(
|
||||
ReactMarkerConstants.FABRIC_BATCH_EXECUTION_START, null, mCommitNumber);
|
||||
}
|
||||
}
|
||||
|
||||
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
|
||||
MountItem mountItem = mMountItems[mountItemIndex];
|
||||
mountItem.execute(mountingManager);
|
||||
}
|
||||
|
||||
private void endMarkers() {
|
||||
if (mCommitNumber > 0) {
|
||||
ReactMarker.logFabricMarker(
|
||||
ReactMarkerConstants.FABRIC_BATCH_EXECUTION_END, null, mCommitNumber);
|
||||
|
@ -68,6 +65,31 @@ public class BatchMountItem implements MountItem {
|
|||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
beginMarkers("mountViews");
|
||||
|
||||
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
|
||||
MountItem mountItem = mMountItems[mountItemIndex];
|
||||
mountItem.execute(mountingManager);
|
||||
}
|
||||
|
||||
endMarkers();
|
||||
}
|
||||
|
||||
public void executeDeletes(@NonNull MountingManager mountingManager) {
|
||||
beginMarkers("deleteViews");
|
||||
|
||||
for (int mountItemIndex = 0; mountItemIndex < mSize; mountItemIndex++) {
|
||||
MountItem mountItem = mMountItems[mountItemIndex];
|
||||
if (mountItem instanceof RemoveDeleteMultiMountItem) {
|
||||
((RemoveDeleteMultiMountItem) mountItem).executeDeletes(mountingManager, true);
|
||||
}
|
||||
}
|
||||
|
||||
endMarkers();
|
||||
}
|
||||
|
||||
public int getRootTag() {
|
||||
return mRootTag;
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.mounting.mountitems;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
|
||||
public class DeleteMountItem implements MountItem {
|
||||
|
||||
private int mReactTag;
|
||||
|
||||
public DeleteMountItem(int reactTag) {
|
||||
mReactTag = reactTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
mountingManager.deleteView(mReactTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeleteMountItem [" + mReactTag + "]";
|
||||
}
|
||||
}
|
|
@ -56,11 +56,30 @@ public class RemoveDeleteMultiMountItem implements MountItem {
|
|||
}
|
||||
|
||||
// After removing all views, delete all views marked for deletion.
|
||||
executeDeletes(mountingManager, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute only deletion operations. When being executed as part of shutdown/stopping surface,
|
||||
* deletion failures can be ignored. For example: if there is a batch of MountItems being executed
|
||||
* as part of stopSurface, a "Create" that is not executed may have a matching "Delete". The
|
||||
* Delete will fail but we can safely ignore it in those cases.
|
||||
*
|
||||
* @param mountingManager
|
||||
* @param ignoreFailures
|
||||
*/
|
||||
public void executeDeletes(@NonNull MountingManager mountingManager, boolean ignoreFailures) {
|
||||
for (int i = 0; i < mMetadata.length; i += 4) {
|
||||
int flags = mMetadata[i + FLAGS_INDEX];
|
||||
if ((flags & DELETE_FLAG) != 0) {
|
||||
int tag = mMetadata[i + TAG_INDEX];
|
||||
mountingManager.deleteView(tag);
|
||||
try {
|
||||
mountingManager.deleteView(tag);
|
||||
} catch (IllegalStateException e) {
|
||||
if (!ignoreFailures) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.mounting.mountitems;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
|
||||
public class RemoveMountItem implements MountItem {
|
||||
|
||||
private int mReactTag;
|
||||
private int mParentReactTag;
|
||||
private int mIndex;
|
||||
|
||||
public RemoveMountItem(int reactTag, int parentReactTag, int index) {
|
||||
mReactTag = reactTag;
|
||||
mParentReactTag = parentReactTag;
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull MountingManager mountingManager) {
|
||||
mountingManager.removeViewAt(-1, mParentReactTag, mIndex);
|
||||
}
|
||||
|
||||
public int getParentReactTag() {
|
||||
return mParentReactTag;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemoveMountItem ["
|
||||
+ mReactTag
|
||||
+ "] - parentTag: "
|
||||
+ mParentReactTag
|
||||
+ " - index: "
|
||||
+ mIndex;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import com.facebook.react.bridge.RetryableMountingLayerException;
|
|||
import com.facebook.react.bridge.SoftAssertions;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.modules.core.ReactChoreographer;
|
||||
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
@ -596,7 +595,6 @@ public class UIViewOperationQueue {
|
|||
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
|
||||
private final ReactApplicationContext mReactApplicationContext;
|
||||
|
||||
private final boolean mAllowViewCommandsQueue;
|
||||
private ArrayList<DispatchCommandViewOperation> mViewCommandOperations = new ArrayList<>();
|
||||
|
||||
// Only called from the UIManager queue?
|
||||
|
@ -637,7 +635,6 @@ public class UIViewOperationQueue {
|
|||
? DEFAULT_MIN_TIME_LEFT_IN_FRAME_FOR_NONBATCHED_OPERATION_MS
|
||||
: minTimeLeftInFrameForNonBatchedOperationMs);
|
||||
mReactApplicationContext = reactContext;
|
||||
mAllowViewCommandsQueue = ReactFeatureFlags.allowEarlyViewCommandExecution;
|
||||
}
|
||||
|
||||
/*package*/ NativeViewHierarchyManager getNativeViewHierarchyManager() {
|
||||
|
@ -709,22 +706,14 @@ public class UIViewOperationQueue {
|
|||
int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
|
||||
final DispatchCommandOperation command =
|
||||
new DispatchCommandOperation(reactTag, commandId, commandArgs);
|
||||
if (mAllowViewCommandsQueue) {
|
||||
mViewCommandOperations.add(command);
|
||||
} else {
|
||||
mOperations.add(command);
|
||||
}
|
||||
mViewCommandOperations.add(command);
|
||||
}
|
||||
|
||||
public void enqueueDispatchCommand(
|
||||
int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
|
||||
final DispatchStringCommandOperation command =
|
||||
new DispatchStringCommandOperation(reactTag, commandId, commandArgs);
|
||||
if (mAllowViewCommandsQueue) {
|
||||
mViewCommandOperations.add(command);
|
||||
} else {
|
||||
mOperations.add(command);
|
||||
}
|
||||
mViewCommandOperations.add(command);
|
||||
}
|
||||
|
||||
public void enqueueUpdateExtraData(int reactTag, Object extraData) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.view.MotionEvent;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStructure;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -347,24 +348,26 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
|
|||
Assertions.assertNotNull(mDialog, "mDialog must exist when we call updateProperties");
|
||||
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null) {
|
||||
int activityWindowFlags = currentActivity.getWindow().getAttributes().flags;
|
||||
if ((activityWindowFlags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) {
|
||||
mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
} else {
|
||||
mDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
Window window = mDialog.getWindow();
|
||||
if (currentActivity == null || currentActivity.isFinishing() || !window.isActive()) {
|
||||
// If the activity has disappeared, then we shouldn't update the window associated to the
|
||||
// Dialog.
|
||||
return;
|
||||
}
|
||||
int activityWindowFlags = currentActivity.getWindow().getAttributes().flags;
|
||||
if ((activityWindowFlags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
|
||||
if (mTransparent) {
|
||||
mDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
} else {
|
||||
mDialog.getWindow().setDimAmount(0.5f);
|
||||
mDialog
|
||||
.getWindow()
|
||||
.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_DIM_BEHIND,
|
||||
WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
window.setDimAmount(0.5f);
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -307,6 +307,8 @@ public class ReactHorizontalScrollViewManager extends ViewGroupManager<ReactHori
|
|||
double x = value.hasKey("x") ? value.getDouble("x") : 0;
|
||||
double y = value.hasKey("y") ? value.getDouble("y") : 0;
|
||||
view.reactScrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y));
|
||||
} else {
|
||||
view.reactScrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,6 +311,8 @@ public class ReactScrollViewManager extends ViewGroupManager<ReactScrollView>
|
|||
double x = value.hasKey("x") ? value.getDouble("x") : 0;
|
||||
double y = value.hasKey("y") ? value.getDouble("y") : 0;
|
||||
view.reactScrollTo((int) PixelUtil.toPixelFromDIP(x), (int) PixelUtil.toPixelFromDIP(y));
|
||||
} else {
|
||||
view.reactScrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
package com.facebook.react.views.text;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Linkify;
|
||||
|
@ -96,6 +97,24 @@ public abstract class ReactTextAnchorViewManager<T extends View, C extends React
|
|||
}
|
||||
}
|
||||
|
||||
@ReactProp(name = "android_hyphenationFrequency")
|
||||
public void setAndroidHyphenationFrequency(ReactTextView view, @Nullable String frequency) {
|
||||
if (frequency == null || frequency.equals("none")) {
|
||||
view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
|
||||
} else if (frequency.equals("full")) {
|
||||
view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
|
||||
} else if (frequency.equals("balanced")) {
|
||||
view.setHyphenationFrequency(Layout.BREAK_STRATEGY_BALANCED);
|
||||
} else if (frequency.equals("high")) {
|
||||
view.setHyphenationFrequency(Layout.BREAK_STRATEGY_HIGH_QUALITY);
|
||||
} else if (frequency.equals("normal")) {
|
||||
view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Invalid android_hyphenationFrequency: " + frequency);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names = {
|
||||
ViewProps.BORDER_RADIUS,
|
||||
|
|
|
@ -860,6 +860,19 @@ public class NativeAnimatedNodeTraversalTest {
|
|||
verifyNoMoreInteractions(animationCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValue() {
|
||||
int tag = 1;
|
||||
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||
tag, JavaOnlyMap.of("type", "value", "value", 1d, "offset", 0d));
|
||||
|
||||
Callback saveValueCallbackMock = mock(Callback.class);
|
||||
|
||||
mNativeAnimatedNodesManager.getValue(tag, saveValueCallbackMock);
|
||||
|
||||
verify(saveValueCallbackMock, times(1)).invoke(1d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterpolationNode() {
|
||||
mNativeAnimatedNodesManager.createAnimatedNode(
|
||||
|
|
|
@ -521,8 +521,8 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
mutation.type == ShadowViewMutation::Type::Remove
|
||||
? mutation.oldChildShadowView
|
||||
: mutation.newChildShadowView);
|
||||
auto const &componentDescriptor =
|
||||
getComponentDescriptorForShadowView(baselineShadowView);
|
||||
bool haveComponentDescriptor =
|
||||
hasComponentDescriptorForShadowView(baselineShadowView);
|
||||
|
||||
auto mutationConfig =
|
||||
(mutation.type == ShadowViewMutation::Type::Delete
|
||||
|
@ -660,8 +660,11 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
AnimationKeyFrame keyFrame{};
|
||||
if (mutation.type == ShadowViewMutation::Type::Insert) {
|
||||
if (mutationConfig->animationProperty ==
|
||||
AnimationProperty::Opacity) {
|
||||
auto props = componentDescriptor.cloneProps(viewStart.props, {});
|
||||
AnimationProperty::Opacity &&
|
||||
haveComponentDescriptor) {
|
||||
auto props =
|
||||
getComponentDescriptorForShadowView(baselineShadowView)
|
||||
.cloneProps(viewStart.props, {});
|
||||
const auto viewProps =
|
||||
dynamic_cast<const ViewProps *>(props.get());
|
||||
if (viewProps != nullptr) {
|
||||
|
@ -675,8 +678,10 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
bool isScaleY = mutationConfig->animationProperty ==
|
||||
AnimationProperty::ScaleY ||
|
||||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
|
||||
if (isScaleX || isScaleY) {
|
||||
auto props = componentDescriptor.cloneProps(viewStart.props, {});
|
||||
if ((isScaleX || isScaleY) && haveComponentDescriptor) {
|
||||
auto props =
|
||||
getComponentDescriptorForShadowView(baselineShadowView)
|
||||
.cloneProps(viewStart.props, {});
|
||||
const auto viewProps =
|
||||
dynamic_cast<const ViewProps *>(props.get());
|
||||
if (viewProps != nullptr) {
|
||||
|
@ -695,8 +700,11 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
0};
|
||||
} else if (mutation.type == ShadowViewMutation::Type::Delete) {
|
||||
if (mutationConfig->animationProperty ==
|
||||
AnimationProperty::Opacity) {
|
||||
auto props = componentDescriptor.cloneProps(viewFinal.props, {});
|
||||
AnimationProperty::Opacity &&
|
||||
haveComponentDescriptor) {
|
||||
auto props =
|
||||
getComponentDescriptorForShadowView(baselineShadowView)
|
||||
.cloneProps(viewFinal.props, {});
|
||||
const auto viewProps =
|
||||
dynamic_cast<const ViewProps *>(props.get());
|
||||
if (viewProps != nullptr) {
|
||||
|
@ -710,8 +718,10 @@ LayoutAnimationKeyFrameManager::pullTransaction(
|
|||
bool isScaleY = mutationConfig->animationProperty ==
|
||||
AnimationProperty::ScaleY ||
|
||||
mutationConfig->animationProperty == AnimationProperty::ScaleXY;
|
||||
if (isScaleX || isScaleY) {
|
||||
auto props = componentDescriptor.cloneProps(viewFinal.props, {});
|
||||
if ((isScaleX || isScaleY) && haveComponentDescriptor) {
|
||||
auto props =
|
||||
getComponentDescriptorForShadowView(baselineShadowView)
|
||||
.cloneProps(viewFinal.props, {});
|
||||
const auto viewProps =
|
||||
dynamic_cast<const ViewProps *>(props.get());
|
||||
if (viewProps != nullptr) {
|
||||
|
@ -983,6 +993,12 @@ bool LayoutAnimationKeyFrameManager::mutatedViewIsVirtual(
|
|||
return viewIsVirtual;
|
||||
}
|
||||
|
||||
bool LayoutAnimationKeyFrameManager::hasComponentDescriptorForShadowView(
|
||||
ShadowView const &shadowView) const {
|
||||
return componentDescriptorRegistry_->hasComponentDescriptorAt(
|
||||
shadowView.componentHandle);
|
||||
}
|
||||
|
||||
ComponentDescriptor const &
|
||||
LayoutAnimationKeyFrameManager::getComponentDescriptorForShadowView(
|
||||
ShadowView const &shadowView) const {
|
||||
|
@ -1008,6 +1024,9 @@ ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView(
|
|||
AnimationConfig const &animationConfig,
|
||||
ShadowView startingView,
|
||||
ShadowView finalView) const {
|
||||
if (!hasComponentDescriptorForShadowView(startingView)) {
|
||||
return finalView;
|
||||
}
|
||||
ComponentDescriptor const &componentDescriptor =
|
||||
getComponentDescriptorForShadowView(startingView);
|
||||
auto mutatedShadowView = ShadowView(startingView);
|
||||
|
|
|
@ -148,6 +148,7 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate,
|
|||
protected:
|
||||
bool mutatedViewIsVirtual(ShadowViewMutation const &mutation) const;
|
||||
|
||||
bool hasComponentDescriptorForShadowView(ShadowView const &shadowView) const;
|
||||
ComponentDescriptor const &getComponentDescriptorForShadowView(
|
||||
ShadowView const &shadowView) const;
|
||||
std::pair<double, double> calculateAnimationProgress(
|
||||
|
|
|
@ -168,6 +168,18 @@ ComponentDescriptor const &ComponentDescriptorRegistry::at(
|
|||
return *_registryByHandle.at(componentHandle);
|
||||
}
|
||||
|
||||
bool ComponentDescriptorRegistry::hasComponentDescriptorAt(
|
||||
ComponentHandle componentHandle) const {
|
||||
std::shared_lock<better::shared_mutex> lock(mutex_);
|
||||
|
||||
auto iterator = _registryByHandle.find(componentHandle);
|
||||
if (iterator == _registryByHandle.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedShadowNode ComponentDescriptorRegistry::createNode(
|
||||
Tag tag,
|
||||
std::string const &viewName,
|
||||
|
|
|
@ -51,6 +51,8 @@ class ComponentDescriptorRegistry {
|
|||
ComponentDescriptor const &at(std::string const &componentName) const;
|
||||
ComponentDescriptor const &at(ComponentHandle componentHandle) const;
|
||||
|
||||
bool hasComponentDescriptorAt(ComponentHandle componentHandle) const;
|
||||
|
||||
ShadowNode::Shared createNode(
|
||||
Tag tag,
|
||||
std::string const &viewName,
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
|
||||
load(
|
||||
"//tools/build_defs/oss:rn_defs.bzl",
|
||||
"APPLE",
|
||||
"get_apple_compiler_flags",
|
||||
"get_apple_inspector_flags",
|
||||
"react_native_xplat_target",
|
||||
"rn_xplat_cxx_library",
|
||||
"subdir_glob",
|
||||
)
|
||||
|
||||
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
|
||||
|
||||
rn_xplat_cxx_library(
|
||||
name = "inputaccessory",
|
||||
srcs = glob(
|
||||
["**/*.cpp"],
|
||||
),
|
||||
headers = [],
|
||||
header_namespace = "",
|
||||
exported_headers = subdir_glob(
|
||||
[
|
||||
("", "*.h"),
|
||||
],
|
||||
prefix = "react/components/inputaccessory",
|
||||
),
|
||||
compiler_flags = [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wall",
|
||||
],
|
||||
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
|
||||
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
|
||||
force_static = True,
|
||||
labels = ["supermodule:xplat/default/public.react_native.infra"],
|
||||
platforms = (APPLE),
|
||||
preprocessor_flags = [
|
||||
"-DLOG_TAG=\"ReactNative\"",
|
||||
"-DWITH_FBSYSTRACE=1",
|
||||
],
|
||||
visibility = ["PUBLIC"],
|
||||
deps = [
|
||||
react_native_xplat_target("fabric/core:core"),
|
||||
"//xplat/js/react-native-github:generated_components-rncore",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/components/inputaccessory/InputAccessoryShadowNode.h>
|
||||
#include <react/core/ConcreteComponentDescriptor.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* Descriptor for <InputAccessoryView> component.
|
||||
*/
|
||||
class InputAccessoryComponentDescriptor final
|
||||
: public ConcreteComponentDescriptor<InputAccessoryShadowNode> {
|
||||
public:
|
||||
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
||||
|
||||
void adopt(UnsharedShadowNode shadowNode) const override {
|
||||
assert(std::dynamic_pointer_cast<InputAccessoryShadowNode>(shadowNode));
|
||||
auto concreteShadowNode =
|
||||
std::static_pointer_cast<InputAccessoryShadowNode>(shadowNode);
|
||||
|
||||
assert(std::dynamic_pointer_cast<YogaLayoutableShadowNode>(
|
||||
concreteShadowNode));
|
||||
auto layoutableShadowNode =
|
||||
std::static_pointer_cast<YogaLayoutableShadowNode>(concreteShadowNode);
|
||||
|
||||
auto state =
|
||||
std::static_pointer_cast<const InputAccessoryShadowNode::ConcreteState>(
|
||||
shadowNode->getState());
|
||||
auto stateData = state->getData();
|
||||
|
||||
layoutableShadowNode->setSize(
|
||||
Size{stateData.screenSize.width, stateData.screenSize.height});
|
||||
layoutableShadowNode->setPositionType(YGPositionTypeAbsolute);
|
||||
|
||||
ConcreteComponentDescriptor::adopt(shadowNode);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "InputAccessoryShadowNode.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
extern const char InputAccessoryComponentName[] = "InputAccessoryView";
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/components/inputaccessory/InputAccessoryState.h>
|
||||
#include <react/components/rncore/EventEmitters.h>
|
||||
#include <react/components/rncore/Props.h>
|
||||
#include <react/components/view/ConcreteViewShadowNode.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
extern const char InputAccessoryComponentName[];
|
||||
|
||||
/*
|
||||
* `ShadowNode` for <InputAccessory> component.
|
||||
*/
|
||||
class InputAccessoryShadowNode final : public ConcreteViewShadowNode<
|
||||
InputAccessoryComponentName,
|
||||
InputAccessoryProps,
|
||||
InputAccessoryEventEmitter,
|
||||
InputAccessoryState> {
|
||||
public:
|
||||
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
||||
|
||||
static ShadowNodeTraits BaseTraits() {
|
||||
auto traits = ConcreteViewShadowNode::BaseTraits();
|
||||
traits.set(ShadowNodeTraits::Trait::RootNodeKind);
|
||||
return traits;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче