react-native-reanimated 2.3.0-beta.2 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Common/cpp/NativeModules/NativeReanimatedModule.cpp +12 -3
- package/Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp +13 -0
- package/Common/cpp/Registries/EventHandlerRegistry.cpp +2 -2
- package/Common/cpp/Registries/MapperRegistry.cpp +5 -5
- package/Common/cpp/SharedItems/FrozenObject.cpp +1 -3
- package/Common/cpp/SharedItems/ShareableValue.cpp +8 -10
- package/Common/cpp/Tools/FeaturesConfig.cpp +5 -0
- package/Common/cpp/Tools/JSIStoreValueUser.cpp +8 -8
- package/Common/cpp/Tools/Mapper.cpp +1 -1
- package/Common/cpp/Tools/RuntimeDecorator.cpp +40 -4
- package/Common/cpp/Tools/WorkletEventHandler.cpp +3 -1
- package/Common/cpp/headers/NativeModules/NativeReanimatedModule.h +5 -0
- package/Common/cpp/headers/NativeModules/NativeReanimatedModuleSpec.h +6 -1
- package/Common/cpp/headers/Registries/EventHandlerRegistry.h +1 -0
- package/Common/cpp/headers/Registries/MapperRegistry.h +1 -0
- package/Common/cpp/headers/SharedItems/FrozenObject.h +4 -0
- package/Common/cpp/headers/SharedItems/HostFunctionHandler.h +2 -0
- package/Common/cpp/headers/SharedItems/MutableValue.h +2 -0
- package/Common/cpp/headers/SharedItems/MutableValueSetterProxy.h +3 -1
- package/Common/cpp/headers/SharedItems/RemoteObject.h +4 -1
- package/Common/cpp/headers/SharedItems/ShareableValue.h +2 -0
- package/Common/cpp/headers/SharedItems/SharedParent.h +9 -9
- package/Common/cpp/headers/SharedItems/ValueWrapper.h +32 -28
- package/Common/cpp/headers/SpecTools/ErrorHandler.h +1 -0
- package/Common/cpp/headers/Tools/FeaturesConfig.h +19 -0
- package/Common/cpp/headers/Tools/JSIStoreValueUser.h +2 -5
- package/Common/cpp/headers/Tools/Mapper.h +2 -0
- package/Common/cpp/headers/Tools/PlatformDepMethodsHolder.h +5 -0
- package/Common/cpp/headers/Tools/ReanimatedHiddenHeaders.h +1 -5
- package/Common/cpp/headers/Tools/RuntimeDecorator.h +10 -7
- package/Common/cpp/headers/Tools/Scheduler.h +2 -0
- package/Common/cpp/headers/Tools/WorkletEventHandler.h +2 -1
- package/Common/cpp/hidden_headers/Logger.h +2 -2
- package/Common/cpp/hidden_headers/LoggerInterface.h +1 -1
- package/README.md +1 -1
- package/RNReanimated.podspec +21 -3
- package/android/build.gradle +33 -13
- package/android/expo/linking.gradle +46 -0
- package/android/expo/src/main/java/com/swmansion/reanimated/EXReanimatedAdapter.java +12 -0
- package/android/expo/src/main/java/expo/modules/adapters/reanimated/EXReanimatedPackage.java +26 -0
- package/android/react-native-reanimated-63-hermes.aar +0 -0
- package/android/react-native-reanimated-63-jsc.aar +0 -0
- package/android/react-native-reanimated-64-hermes.aar +0 -0
- package/android/react-native-reanimated-64-jsc.aar +0 -0
- package/android/react-native-reanimated-65-hermes.aar +0 -0
- package/android/react-native-reanimated-65-jsc.aar +0 -0
- package/android/react-native-reanimated-66-hermes.aar +0 -0
- package/android/react-native-reanimated-66-jsc.aar +0 -0
- package/android/react-native-reanimated-67-hermes.aar +0 -0
- package/android/react-native-reanimated-67-jsc.aar +0 -0
- package/expo-module.config.json +3 -0
- package/ios/LayoutReanimation/REAAnimationsManager.m +65 -19
- package/ios/LayoutReanimation/REAUIManager.mm +101 -61
- package/ios/Nodes/REAPropsNode.m +1 -1
- package/ios/REAEventDispatcher.m +1 -1
- package/ios/REAModule.h +0 -2
- package/ios/REAModule.m +0 -3
- package/ios/REANodesManager.m +1 -0
- package/ios/RNGestureHandlerStateManager.h +5 -0
- package/ios/native/NativeMethods.h +5 -0
- package/ios/native/NativeMethods.mm +14 -6
- package/ios/native/NativeProxy.h +1 -0
- package/ios/native/NativeProxy.mm +84 -63
- package/ios/native/REAIOSErrorHandler.mm +19 -15
- package/ios/native/REAIOSLogger.mm +10 -6
- package/ios/native/REAIOSScheduler.mm +17 -10
- package/ios/native/REAInitializer.mm +39 -37
- package/ios/native/UIResponder+Reanimated.mm +2 -2
- package/lib/Animated.js +11 -5
- package/lib/ConfigHelper.js +121 -138
- package/lib/ReanimatedEventEmitter.js +0 -1
- package/lib/ReanimatedModule.js +0 -1
- package/lib/ReanimatedModule.macos.js +0 -1
- package/lib/ReanimatedModule.native.js +5 -6
- package/lib/ReanimatedModule.windows.js +0 -1
- package/lib/ReanimatedModuleCompat.js +64 -35
- package/lib/createAnimatedComponent.js +469 -499
- package/lib/index.js +11 -5
- package/lib/reanimated1/core/Core.test.js +1 -1
- package/lib/reanimated2/NativeMethods.js +7 -0
- package/lib/reanimated2/NativeReanimated/NativeReanimated.js +3 -0
- package/lib/reanimated2/PropAdapters.js +0 -1
- package/lib/reanimated2/UpdateProps.js +1 -1
- package/lib/reanimated2/ViewDescriptorsSet.js +4 -2
- package/lib/reanimated2/animation/spring.js +2 -0
- package/lib/reanimated2/animation/styleAnimation.js +5 -1
- package/lib/reanimated2/animation/util.js +23 -0
- package/lib/reanimated2/component/FlatList.js +30 -0
- package/lib/reanimated2/component/WrappedComponents.js +9 -0
- package/lib/reanimated2/component/index.js +4 -0
- package/lib/reanimated2/core.js +34 -6
- package/lib/reanimated2/globals.d.ts +8 -0
- package/lib/reanimated2/js-reanimated/JSReanimated.js +4 -0
- package/lib/reanimated2/js-reanimated/Mapper.js +4 -3
- package/lib/reanimated2/js-reanimated/MapperRegistry.js +3 -6
- package/lib/reanimated2/js-reanimated/MutableValue.js +0 -2
- package/lib/reanimated2/js-reanimated/index.js +10 -8
- package/lib/reanimated2/layoutReanimation/LayoutAnimationRepository.js +14 -5
- package/lib/reanimated2/layoutReanimation/defaultAnimations/Default.js +14 -14
- package/lib/reanimated2/layoutReanimation/defaultAnimations/Flip.js +8 -8
- package/lib/reanimated2/layoutReanimation/defaultAnimations/Rotate.js +16 -16
- package/lib/reanimated2/layoutReanimation/defaultAnimations/Slide.js +16 -16
- package/lib/reanimated2/layoutReanimation/defaultAnimations/Zoom.js +4 -4
- package/lib/reanimated2/layoutReanimation/defaultTransitions/CurvedTransition.js +20 -8
- package/lib/reanimated2/layoutReanimation/defaultTransitions/EntryExitTransition.js +3 -3
- package/lib/reanimated2/layoutReanimation/defaultTransitions/FadingTransition.js +8 -8
- package/lib/reanimated2/layoutReanimation/defaultTransitions/JumpingTransition.js +10 -10
- package/lib/reanimated2/layoutReanimation/defaultTransitions/LinearTransition.js +8 -8
- package/lib/reanimated2/layoutReanimation/defaultTransitions/SequencedTransition.js +8 -8
- package/lib/reanimated2/mock.js +3 -1
- package/lib/setAndForwardRef.js +14 -48
- package/mock.js +1 -1
- package/package.json +27 -23
- package/plugin.js +111 -110
- package/react-native-reanimated.d.ts +56 -27
- package/src/Animated.js +11 -5
- package/src/{ConfigHelper.js → ConfigHelper.ts} +15 -7
- package/src/{ReanimatedEventEmitter.js → ReanimatedEventEmitter.ts} +0 -0
- package/src/{ReanimatedModule.js → ReanimatedModule.macos.ts} +0 -0
- package/src/{ReanimatedModule.native.js → ReanimatedModule.native.ts} +0 -0
- package/src/{ReanimatedModule.macos.js → ReanimatedModule.ts} +0 -0
- package/src/{ReanimatedModule.windows.js → ReanimatedModule.windows.ts} +0 -0
- package/src/ReanimatedModuleCompat.ts +51 -0
- package/src/{createAnimatedComponent.js → createAnimatedComponent.tsx} +278 -124
- package/src/reanimated1/core/Core.test.js +1 -1
- package/src/reanimated2/NativeMethods.ts +13 -1
- package/src/reanimated2/NativeReanimated/NativeReanimated.ts +5 -11
- package/src/reanimated2/PropAdapters.ts +0 -1
- package/src/reanimated2/UpdateProps.ts +1 -1
- package/src/reanimated2/ViewDescriptorsSet.ts +6 -2
- package/src/reanimated2/animation/commonTypes.ts +4 -4
- package/src/reanimated2/animation/decay.ts +2 -2
- package/src/reanimated2/animation/delay.ts +3 -3
- package/src/reanimated2/animation/index.ts +7 -7
- package/src/reanimated2/animation/repeat.ts +4 -4
- package/src/reanimated2/animation/sequence.ts +2 -2
- package/src/reanimated2/animation/spring.ts +6 -5
- package/src/reanimated2/animation/styleAnimation.ts +24 -12
- package/src/reanimated2/animation/timing.ts +5 -5
- package/src/reanimated2/animation/util.ts +45 -6
- package/src/reanimated2/component/FlatList.tsx +44 -0
- package/src/reanimated2/component/WrappedComponents.ts +11 -0
- package/src/reanimated2/component/index.ts +9 -0
- package/src/reanimated2/core.ts +62 -17
- package/src/reanimated2/globals.d.ts +8 -0
- package/src/reanimated2/hook/useAnimatedStyle.ts +2 -2
- package/src/reanimated2/js-reanimated/JSReanimated.ts +15 -26
- package/src/reanimated2/js-reanimated/Mapper.ts +22 -9
- package/src/reanimated2/js-reanimated/MapperRegistry.ts +22 -19
- package/src/reanimated2/js-reanimated/MutableValue.ts +11 -11
- package/src/reanimated2/js-reanimated/index.ts +31 -14
- package/src/reanimated2/layoutReanimation/{LayoutAnimationRepository.tsx → LayoutAnimationRepository.ts} +18 -5
- package/src/reanimated2/layoutReanimation/animationBuilder/commonTypes.ts +39 -20
- package/src/reanimated2/layoutReanimation/animationBuilder/index.ts +5 -1
- package/src/reanimated2/layoutReanimation/defaultAnimations/Default.ts +21 -15
- package/src/reanimated2/layoutReanimation/defaultAnimations/Flip.ts +29 -24
- package/src/reanimated2/layoutReanimation/defaultAnimations/Rotate.ts +61 -34
- package/src/reanimated2/layoutReanimation/defaultAnimations/Slide.ts +49 -34
- package/src/reanimated2/layoutReanimation/defaultAnimations/Zoom.ts +17 -12
- package/src/reanimated2/layoutReanimation/defaultTransitions/CurvedTransition.ts +20 -8
- package/src/reanimated2/layoutReanimation/defaultTransitions/EntryExitTransition.ts +9 -9
- package/src/reanimated2/layoutReanimation/defaultTransitions/FadingTransition.ts +8 -8
- package/src/reanimated2/layoutReanimation/defaultTransitions/JumpingTransition.ts +20 -14
- package/src/reanimated2/layoutReanimation/defaultTransitions/LinearTransition.ts +14 -8
- package/src/reanimated2/layoutReanimation/defaultTransitions/SequencedTransition.ts +24 -12
- package/src/reanimated2/mock.ts +3 -1
- package/src/{setAndForwardRef.js → setAndForwardRef.ts} +12 -2
- package/src/ReanimatedModuleCompat.js +0 -37
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { Component, ComponentType, MutableRefObject, Ref } from 'react';
|
|
2
2
|
import { findNodeHandle, Platform, StyleSheet } from 'react-native';
|
|
3
3
|
import ReanimatedEventEmitter from './ReanimatedEventEmitter';
|
|
4
4
|
|
|
5
|
+
// @ts-ignore JS file
|
|
5
6
|
import AnimatedEvent from './reanimated1/core/AnimatedEvent';
|
|
7
|
+
// @ts-ignore JS file
|
|
6
8
|
import AnimatedNode from './reanimated1/core/AnimatedNode';
|
|
9
|
+
// @ts-ignore JS file
|
|
7
10
|
import AnimatedValue from './reanimated1/core/AnimatedValue';
|
|
11
|
+
// @ts-ignore JS file
|
|
8
12
|
import { createOrReusePropsNode } from './reanimated1/core/AnimatedProps';
|
|
9
13
|
import WorkletEventHandler from './reanimated2/WorkletEventHandler';
|
|
10
14
|
import setAndForwardRef from './setAndForwardRef';
|
|
@@ -13,18 +17,41 @@ import './reanimated2/layoutReanimation/LayoutAnimationRepository';
|
|
|
13
17
|
import invariant from 'invariant';
|
|
14
18
|
import { adaptViewConfig } from './ConfigHelper';
|
|
15
19
|
import { RNRenderer } from './reanimated2/platform-specific/RNRenderer';
|
|
16
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
makeMutable,
|
|
22
|
+
runOnUI,
|
|
23
|
+
enableLayoutAnimations,
|
|
24
|
+
} from './reanimated2/core';
|
|
17
25
|
import {
|
|
18
26
|
DefaultEntering,
|
|
19
27
|
DefaultExiting,
|
|
20
28
|
DefaultLayout,
|
|
21
29
|
} from './reanimated2/layoutReanimation/defaultAnimations/Default';
|
|
22
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
isJest,
|
|
32
|
+
isChromeDebugger,
|
|
33
|
+
shouldBeUseWeb,
|
|
34
|
+
} from './reanimated2/PlatformChecker';
|
|
23
35
|
import { initialUpdaterRun } from './reanimated2/animation';
|
|
36
|
+
import {
|
|
37
|
+
BaseAnimationBuilder,
|
|
38
|
+
EntryExitAnimationFunction,
|
|
39
|
+
ILayoutAnimationBuilder,
|
|
40
|
+
} from './reanimated2/layoutReanimation';
|
|
41
|
+
import { SharedValue, StyleProps } from './reanimated2/commonTypes';
|
|
42
|
+
import {
|
|
43
|
+
ViewDescriptorsSet,
|
|
44
|
+
ViewRefSet,
|
|
45
|
+
} from './reanimated2/ViewDescriptorsSet';
|
|
24
46
|
|
|
25
47
|
const NODE_MAPPING = new Map();
|
|
26
48
|
|
|
27
|
-
|
|
49
|
+
interface ListenerData {
|
|
50
|
+
viewTag: number;
|
|
51
|
+
props: StyleProps;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function listener(data: ListenerData) {
|
|
28
55
|
const component = NODE_MAPPING.get(data.viewTag);
|
|
29
56
|
component && component._updateFromNative(data.props);
|
|
30
57
|
}
|
|
@@ -34,7 +61,7 @@ function dummyListener() {
|
|
|
34
61
|
// event is used.
|
|
35
62
|
}
|
|
36
63
|
|
|
37
|
-
function hasAnimatedNodes(value) {
|
|
64
|
+
function hasAnimatedNodes(value: unknown): boolean {
|
|
38
65
|
if (value instanceof AnimatedNode) {
|
|
39
66
|
return true;
|
|
40
67
|
}
|
|
@@ -42,18 +69,21 @@ function hasAnimatedNodes(value) {
|
|
|
42
69
|
return value.some((item) => hasAnimatedNodes(item));
|
|
43
70
|
}
|
|
44
71
|
if (value && typeof value === 'object') {
|
|
45
|
-
return Object.keys(value).some((key) =>
|
|
72
|
+
return Object.keys(value).some((key) =>
|
|
73
|
+
hasAnimatedNodes((value as Record<string, unknown>)[key])
|
|
74
|
+
);
|
|
46
75
|
}
|
|
47
76
|
return false;
|
|
48
77
|
}
|
|
49
78
|
|
|
50
|
-
|
|
79
|
+
type NestedArray<T> = T | NestedArray<T>[];
|
|
80
|
+
function flattenArray<T>(array: NestedArray<T>): T[] {
|
|
51
81
|
if (!Array.isArray(array)) {
|
|
52
82
|
return [array];
|
|
53
83
|
}
|
|
54
|
-
const resultArr = [];
|
|
84
|
+
const resultArr: T[] = [];
|
|
55
85
|
|
|
56
|
-
const _flattenArray = (arr) => {
|
|
86
|
+
const _flattenArray = (arr: NestedArray<T>[]): void => {
|
|
57
87
|
arr.forEach((item) => {
|
|
58
88
|
if (Array.isArray(item)) {
|
|
59
89
|
_flattenArray(item);
|
|
@@ -66,7 +96,88 @@ function flattenArray(array) {
|
|
|
66
96
|
return resultArr;
|
|
67
97
|
}
|
|
68
98
|
|
|
69
|
-
|
|
99
|
+
function onlyAnimatedStyles(styles: StyleProps[]) {
|
|
100
|
+
return styles.filter((style) => style?.viewDescriptors);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isSameAnimatedStyle(
|
|
104
|
+
style1?: StyleProps,
|
|
105
|
+
style2?: StyleProps
|
|
106
|
+
): boolean {
|
|
107
|
+
// We cannot use equality check to compare useAnimatedStyle outputs directly.
|
|
108
|
+
// Instead, we can compare its viewsRefs.
|
|
109
|
+
return style1?.viewsRef === style2?.viewsRef;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const isSameAnimatedProps = isSameAnimatedStyle;
|
|
113
|
+
|
|
114
|
+
const has = <K extends string>(
|
|
115
|
+
key: K,
|
|
116
|
+
x: unknown
|
|
117
|
+
): x is { [key in K]: unknown } => {
|
|
118
|
+
if (typeof x === 'function' || typeof x === 'object') {
|
|
119
|
+
if (x === null || x === undefined) {
|
|
120
|
+
return false;
|
|
121
|
+
} else {
|
|
122
|
+
return key in x;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
interface AnimatedProps extends Record<string, unknown> {
|
|
129
|
+
viewDescriptors?: ViewDescriptorsSet;
|
|
130
|
+
viewsRef?: ViewRefSet<unknown>;
|
|
131
|
+
initial?: SharedValue<StyleProps>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type AnimatedComponentProps<P extends Record<string, unknown>> = P & {
|
|
135
|
+
forwardedRef?: Ref<Component>;
|
|
136
|
+
style?: NestedArray<StyleProps>;
|
|
137
|
+
animatedProps?: Partial<AnimatedComponentProps<AnimatedProps>>;
|
|
138
|
+
animatedStyle?: StyleProps;
|
|
139
|
+
layout?:
|
|
140
|
+
| BaseAnimationBuilder
|
|
141
|
+
| ILayoutAnimationBuilder
|
|
142
|
+
| typeof BaseAnimationBuilder;
|
|
143
|
+
entering?:
|
|
144
|
+
| BaseAnimationBuilder
|
|
145
|
+
| typeof BaseAnimationBuilder
|
|
146
|
+
| EntryExitAnimationFunction
|
|
147
|
+
| Keyframe;
|
|
148
|
+
exiting?:
|
|
149
|
+
| BaseAnimationBuilder
|
|
150
|
+
| typeof BaseAnimationBuilder
|
|
151
|
+
| EntryExitAnimationFunction
|
|
152
|
+
| Keyframe;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
type Options<P> = {
|
|
156
|
+
setNativeProps: (ref: ComponentRef, props: P) => void;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
interface ComponentRef extends Component {
|
|
160
|
+
setNativeProps?: (props: Record<string, unknown>) => void;
|
|
161
|
+
getScrollableNode?: () => ComponentRef;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface InitialComponentProps extends Record<string, unknown> {
|
|
165
|
+
ref?: Ref<Component>;
|
|
166
|
+
collapsable?: boolean;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interface PropsAnimated {
|
|
170
|
+
__onEvaluate: () => StyleProps;
|
|
171
|
+
__detach: () => void;
|
|
172
|
+
__getValue: () => StyleProps;
|
|
173
|
+
update: () => void;
|
|
174
|
+
setNativeView: (view: Component) => void;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default function createAnimatedComponent(
|
|
178
|
+
Component: ComponentType<InitialComponentProps>,
|
|
179
|
+
options?: Options<InitialComponentProps>
|
|
180
|
+
): ComponentType<AnimatedComponentProps<InitialComponentProps>> {
|
|
70
181
|
invariant(
|
|
71
182
|
typeof Component !== 'function' ||
|
|
72
183
|
(Component.prototype && Component.prototype.isReactComponent),
|
|
@@ -74,13 +185,21 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
74
185
|
'use a class component instead.'
|
|
75
186
|
);
|
|
76
187
|
|
|
77
|
-
class AnimatedComponent extends React.Component
|
|
188
|
+
class AnimatedComponent extends React.Component<
|
|
189
|
+
AnimatedComponentProps<InitialComponentProps>
|
|
190
|
+
> {
|
|
78
191
|
_invokeAnimatedPropsCallbackOnMount = false;
|
|
79
|
-
_styles = null;
|
|
192
|
+
_styles: StyleProps[] | null = null;
|
|
193
|
+
_animatedProps?: Partial<AnimatedComponentProps<AnimatedProps>>;
|
|
80
194
|
_viewTag = -1;
|
|
81
195
|
_isFirstRender = true;
|
|
196
|
+
animatedStyle: { value: StyleProps } = { value: {} };
|
|
197
|
+
sv: SharedValue<null | Record<string, unknown>> | null;
|
|
198
|
+
_propsAnimated?: PropsAnimated;
|
|
199
|
+
_component: ComponentRef | null = null;
|
|
200
|
+
static displayName: string;
|
|
82
201
|
|
|
83
|
-
constructor(props) {
|
|
202
|
+
constructor(props: AnimatedComponentProps<InitialComponentProps>) {
|
|
84
203
|
super(props);
|
|
85
204
|
this._attachProps(this.props);
|
|
86
205
|
if (isJest()) {
|
|
@@ -103,7 +222,9 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
103
222
|
this._animatedPropsCallback();
|
|
104
223
|
}
|
|
105
224
|
|
|
106
|
-
this._propsAnimated &&
|
|
225
|
+
this._propsAnimated &&
|
|
226
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
227
|
+
this._propsAnimated.setNativeView(this._component!);
|
|
107
228
|
this._attachNativeEvents();
|
|
108
229
|
this._attachPropUpdater();
|
|
109
230
|
this._attachAnimatedStyles();
|
|
@@ -112,23 +233,23 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
112
233
|
_getEventViewRef() {
|
|
113
234
|
// Make sure to get the scrollable node for components that implement
|
|
114
235
|
// `ScrollResponder.Mixin`.
|
|
115
|
-
return this._component
|
|
236
|
+
return this._component?.getScrollableNode
|
|
116
237
|
? this._component.getScrollableNode()
|
|
117
238
|
: this._component;
|
|
118
239
|
}
|
|
119
240
|
|
|
120
241
|
_attachNativeEvents() {
|
|
121
242
|
const node = this._getEventViewRef();
|
|
122
|
-
const viewTag = findNodeHandle(options
|
|
243
|
+
const viewTag = findNodeHandle(options?.setNativeProps ? this : node);
|
|
123
244
|
for (const key in this.props) {
|
|
124
245
|
const prop = this.props[key];
|
|
125
246
|
if (prop instanceof AnimatedEvent) {
|
|
126
|
-
prop.attachEvent(node, key);
|
|
247
|
+
(prop as AnimatedEvent).attachEvent(node, key);
|
|
127
248
|
} else if (
|
|
128
|
-
|
|
249
|
+
has('current', prop) &&
|
|
129
250
|
prop.current instanceof WorkletEventHandler
|
|
130
251
|
) {
|
|
131
|
-
prop.current.registerForEvents(viewTag, key);
|
|
252
|
+
prop.current.registerForEvents(viewTag as number, key);
|
|
132
253
|
}
|
|
133
254
|
}
|
|
134
255
|
}
|
|
@@ -139,9 +260,9 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
139
260
|
for (const key in this.props) {
|
|
140
261
|
const prop = this.props[key];
|
|
141
262
|
if (prop instanceof AnimatedEvent) {
|
|
142
|
-
prop.detachEvent(node, key);
|
|
263
|
+
(prop as AnimatedEvent).detachEvent(node, key);
|
|
143
264
|
} else if (
|
|
144
|
-
|
|
265
|
+
has('current', prop) &&
|
|
145
266
|
prop.current instanceof WorkletEventHandler
|
|
146
267
|
) {
|
|
147
268
|
prop.current.unregisterFromEvents();
|
|
@@ -150,17 +271,15 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
150
271
|
}
|
|
151
272
|
|
|
152
273
|
_detachStyles() {
|
|
153
|
-
if (Platform.OS === 'web') {
|
|
274
|
+
if (Platform.OS === 'web' && this._styles !== null) {
|
|
154
275
|
for (const style of this._styles) {
|
|
155
276
|
if (style?.viewsRef) {
|
|
156
277
|
style.viewsRef.remove(this);
|
|
157
278
|
}
|
|
158
279
|
}
|
|
159
|
-
} else if (this._viewTag !== -1) {
|
|
280
|
+
} else if (this._viewTag !== -1 && this._styles !== null) {
|
|
160
281
|
for (const style of this._styles) {
|
|
161
|
-
|
|
162
|
-
style.viewDescriptors.remove(this._viewTag);
|
|
163
|
-
}
|
|
282
|
+
style.viewDescriptors.remove(this._viewTag);
|
|
164
283
|
}
|
|
165
284
|
if (this.props.animatedProps?.viewDescriptors) {
|
|
166
285
|
this.props.animatedProps.viewDescriptors.remove(this._viewTag);
|
|
@@ -168,18 +287,20 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
168
287
|
}
|
|
169
288
|
}
|
|
170
289
|
|
|
171
|
-
_reattachNativeEvents(
|
|
290
|
+
_reattachNativeEvents(
|
|
291
|
+
prevProps: AnimatedComponentProps<InitialComponentProps>
|
|
292
|
+
) {
|
|
172
293
|
const node = this._getEventViewRef();
|
|
173
294
|
const attached = new Set();
|
|
174
295
|
const nextEvts = new Set();
|
|
175
|
-
let viewTag;
|
|
296
|
+
let viewTag: number | undefined;
|
|
176
297
|
|
|
177
298
|
for (const key in this.props) {
|
|
178
299
|
const prop = this.props[key];
|
|
179
300
|
if (prop instanceof AnimatedEvent) {
|
|
180
|
-
nextEvts.add(prop.__nodeID);
|
|
301
|
+
nextEvts.add((prop as AnimatedEvent).__nodeID);
|
|
181
302
|
} else if (
|
|
182
|
-
|
|
303
|
+
has('current', prop) &&
|
|
183
304
|
prop.current instanceof WorkletEventHandler
|
|
184
305
|
) {
|
|
185
306
|
if (viewTag === undefined) {
|
|
@@ -190,15 +311,15 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
190
311
|
for (const key in prevProps) {
|
|
191
312
|
const prop = this.props[key];
|
|
192
313
|
if (prop instanceof AnimatedEvent) {
|
|
193
|
-
if (!nextEvts.has(prop.__nodeID)) {
|
|
314
|
+
if (!nextEvts.has((prop as AnimatedEvent).__nodeID)) {
|
|
194
315
|
// event was in prev props but not in current props, we detach
|
|
195
|
-
prop.detachEvent(node, key);
|
|
316
|
+
(prop as AnimatedEvent).detachEvent(node, key);
|
|
196
317
|
} else {
|
|
197
318
|
// event was in prev and is still in current props
|
|
198
|
-
attached.add(prop.__nodeID);
|
|
319
|
+
attached.add((prop as AnimatedEvent).__nodeID);
|
|
199
320
|
}
|
|
200
321
|
} else if (
|
|
201
|
-
|
|
322
|
+
has('current', prop) &&
|
|
202
323
|
prop.current instanceof WorkletEventHandler &&
|
|
203
324
|
prop.current.reattachNeeded
|
|
204
325
|
) {
|
|
@@ -208,15 +329,19 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
208
329
|
|
|
209
330
|
for (const key in this.props) {
|
|
210
331
|
const prop = this.props[key];
|
|
211
|
-
if (
|
|
332
|
+
if (
|
|
333
|
+
prop instanceof AnimatedEvent &&
|
|
334
|
+
!attached.has((prop as AnimatedEvent).__nodeID)
|
|
335
|
+
) {
|
|
212
336
|
// not yet attached
|
|
213
|
-
prop.attachEvent(node, key);
|
|
337
|
+
(prop as AnimatedEvent).attachEvent(node, key);
|
|
214
338
|
} else if (
|
|
215
|
-
|
|
339
|
+
has('current', prop) &&
|
|
216
340
|
prop.current instanceof WorkletEventHandler &&
|
|
217
341
|
prop.current.reattachNeeded
|
|
218
342
|
) {
|
|
219
|
-
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
344
|
+
prop.current.registerForEvents(viewTag!, key);
|
|
220
345
|
prop.current.reattachNeeded = false;
|
|
221
346
|
}
|
|
222
347
|
}
|
|
@@ -238,11 +363,12 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
238
363
|
} else if (typeof this._component.setNativeProps !== 'function') {
|
|
239
364
|
this.forceUpdate();
|
|
240
365
|
} else {
|
|
241
|
-
|
|
366
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
367
|
+
this._component.setNativeProps(this._propsAnimated!.__getValue());
|
|
242
368
|
}
|
|
243
369
|
};
|
|
244
370
|
|
|
245
|
-
_attachProps(nextProps) {
|
|
371
|
+
_attachProps(nextProps: StyleProps) {
|
|
246
372
|
const oldPropsAnimated = this._propsAnimated;
|
|
247
373
|
|
|
248
374
|
this._propsAnimated = createOrReusePropsNode(
|
|
@@ -264,12 +390,13 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
264
390
|
}
|
|
265
391
|
}
|
|
266
392
|
|
|
267
|
-
_updateFromNative(props) {
|
|
268
|
-
if (options
|
|
269
|
-
|
|
393
|
+
_updateFromNative(props: StyleProps) {
|
|
394
|
+
if (options?.setNativeProps) {
|
|
395
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
396
|
+
options.setNativeProps(this._component!, props);
|
|
270
397
|
} else {
|
|
271
398
|
// eslint-disable-next-line no-unused-expressions
|
|
272
|
-
this._component
|
|
399
|
+
this._component?.setNativeProps?.(props);
|
|
273
400
|
}
|
|
274
401
|
}
|
|
275
402
|
|
|
@@ -282,9 +409,17 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
282
409
|
}
|
|
283
410
|
|
|
284
411
|
_attachAnimatedStyles() {
|
|
285
|
-
const styles =
|
|
412
|
+
const styles = this.props.style
|
|
413
|
+
? onlyAnimatedStyles(flattenArray<StyleProps>(this.props.style))
|
|
414
|
+
: [];
|
|
415
|
+
const prevStyles = this._styles;
|
|
286
416
|
this._styles = styles;
|
|
287
|
-
|
|
417
|
+
|
|
418
|
+
const prevAnimatedProps = this._animatedProps;
|
|
419
|
+
this._animatedProps = this.props.animatedProps;
|
|
420
|
+
|
|
421
|
+
let viewTag: number | null;
|
|
422
|
+
let viewName: string | null;
|
|
288
423
|
if (Platform.OS === 'web') {
|
|
289
424
|
viewTag = findNodeHandle(this);
|
|
290
425
|
viewName = null;
|
|
@@ -304,58 +439,71 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
304
439
|
*/
|
|
305
440
|
viewName = hostInstance?.viewConfig?.uiViewClassName;
|
|
306
441
|
// update UI props whitelist for this view
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
hostInstance.viewConfig
|
|
311
|
-
) {
|
|
442
|
+
const hasReanimated2Props =
|
|
443
|
+
this.props.animatedProps?.viewDescriptors || styles.length;
|
|
444
|
+
if (hasReanimated2Props && hostInstance?.viewConfig) {
|
|
312
445
|
adaptViewConfig(hostInstance.viewConfig);
|
|
313
446
|
}
|
|
314
447
|
}
|
|
315
|
-
this._viewTag = viewTag;
|
|
448
|
+
this._viewTag = viewTag as number;
|
|
449
|
+
|
|
450
|
+
// remove old styles
|
|
451
|
+
if (prevStyles) {
|
|
452
|
+
// in most of the cases, views have only a single animated style and it remains unchanged
|
|
453
|
+
const hasOneSameStyle =
|
|
454
|
+
styles.length === 1 &&
|
|
455
|
+
prevStyles.length === 1 &&
|
|
456
|
+
isSameAnimatedStyle(styles[0], prevStyles[0]);
|
|
457
|
+
|
|
458
|
+
if (!hasOneSameStyle) {
|
|
459
|
+
// otherwise, remove each style that is not present in new styles
|
|
460
|
+
for (const prevStyle of prevStyles) {
|
|
461
|
+
const isPresent = styles.some((style) =>
|
|
462
|
+
isSameAnimatedStyle(style, prevStyle)
|
|
463
|
+
);
|
|
464
|
+
if (!isPresent) {
|
|
465
|
+
prevStyle.viewDescriptors.remove(viewTag);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
316
470
|
|
|
317
471
|
styles.forEach((style) => {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this.animatedStyle.value
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
style.animatedStyle.current = this.animatedStyle;
|
|
332
|
-
}
|
|
472
|
+
style.viewDescriptors.add({ tag: viewTag, name: viewName });
|
|
473
|
+
if (isJest()) {
|
|
474
|
+
/**
|
|
475
|
+
* We need to connect Jest's TestObject instance whose contains just props object
|
|
476
|
+
* with the updateProps() function where we update the properties of the component.
|
|
477
|
+
* We can't update props object directly because TestObject contains a copy of props - look at render function:
|
|
478
|
+
* const props = this._filterNonAnimatedProps(this.props);
|
|
479
|
+
*/
|
|
480
|
+
this.animatedStyle.value = {
|
|
481
|
+
...this.animatedStyle.value,
|
|
482
|
+
...style.initial.value,
|
|
483
|
+
};
|
|
484
|
+
style.animatedStyle.current = this.animatedStyle;
|
|
333
485
|
}
|
|
334
486
|
});
|
|
487
|
+
|
|
488
|
+
// detach old animatedProps
|
|
489
|
+
if (
|
|
490
|
+
prevAnimatedProps &&
|
|
491
|
+
!isSameAnimatedProps(prevAnimatedProps, this.props.animatedProps)
|
|
492
|
+
) {
|
|
493
|
+
prevAnimatedProps.viewDescriptors!.remove(viewTag as number);
|
|
494
|
+
}
|
|
495
|
+
|
|
335
496
|
// attach animatedProps property
|
|
336
497
|
if (this.props.animatedProps?.viewDescriptors) {
|
|
337
498
|
this.props.animatedProps.viewDescriptors.add({
|
|
338
|
-
|
|
339
|
-
|
|
499
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
500
|
+
tag: viewTag!,
|
|
501
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
502
|
+
name: viewName!,
|
|
340
503
|
});
|
|
341
504
|
}
|
|
342
505
|
}
|
|
343
506
|
|
|
344
|
-
_hasReanimated2Props(flattenStyles) {
|
|
345
|
-
if (this.props.animatedProps?.viewDescriptors) {
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
348
|
-
if (this.props.style) {
|
|
349
|
-
for (const style of flattenStyles) {
|
|
350
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
351
|
-
if (style?.hasOwnProperty('viewDescriptors')) {
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
507
|
_detachPropUpdater() {
|
|
360
508
|
const viewTag = findNodeHandle(this);
|
|
361
509
|
NODE_MAPPING.delete(viewTag);
|
|
@@ -364,16 +512,23 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
364
512
|
}
|
|
365
513
|
}
|
|
366
514
|
|
|
367
|
-
componentDidUpdate(
|
|
515
|
+
componentDidUpdate(
|
|
516
|
+
prevProps: AnimatedComponentProps<InitialComponentProps>
|
|
517
|
+
) {
|
|
368
518
|
this._attachProps(this.props);
|
|
369
519
|
this._reattachNativeEvents(prevProps);
|
|
370
520
|
|
|
371
|
-
this._propsAnimated &&
|
|
521
|
+
this._propsAnimated &&
|
|
522
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
523
|
+
this._propsAnimated.setNativeView(this._component!);
|
|
372
524
|
this._attachAnimatedStyles();
|
|
373
525
|
}
|
|
374
526
|
|
|
375
|
-
_setComponentRef = setAndForwardRef({
|
|
376
|
-
getForwardedRef: () =>
|
|
527
|
+
_setComponentRef = setAndForwardRef<Component>({
|
|
528
|
+
getForwardedRef: () =>
|
|
529
|
+
this.props.forwardedRef as MutableRefObject<
|
|
530
|
+
Component<Record<string, unknown>, Record<string, unknown>, unknown>
|
|
531
|
+
>,
|
|
377
532
|
setLocalRef: (ref) => {
|
|
378
533
|
// TODO update config
|
|
379
534
|
const tag = findNodeHandle(ref);
|
|
@@ -381,6 +536,9 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
381
536
|
(this.props.layout || this.props.entering || this.props.exiting) &&
|
|
382
537
|
tag != null
|
|
383
538
|
) {
|
|
539
|
+
if (!shouldBeUseWeb()) {
|
|
540
|
+
enableLayoutAnimations(true, false);
|
|
541
|
+
}
|
|
384
542
|
let layout = this.props.layout ? this.props.layout : DefaultLayout;
|
|
385
543
|
let entering = this.props.entering
|
|
386
544
|
? this.props.entering
|
|
@@ -389,16 +547,16 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
389
547
|
? this.props.exiting
|
|
390
548
|
: DefaultExiting;
|
|
391
549
|
|
|
392
|
-
if (layout
|
|
550
|
+
if (has('build', layout)) {
|
|
393
551
|
layout = layout.build();
|
|
394
552
|
}
|
|
395
553
|
|
|
396
|
-
if (entering
|
|
397
|
-
entering = entering.build();
|
|
554
|
+
if (has('build', entering)) {
|
|
555
|
+
entering = entering.build() as EntryExitAnimationFunction;
|
|
398
556
|
}
|
|
399
557
|
|
|
400
|
-
if (exiting
|
|
401
|
-
exiting = exiting.build();
|
|
558
|
+
if (has('build', exiting)) {
|
|
559
|
+
exiting = exiting.build() as EntryExitAnimationFunction;
|
|
402
560
|
}
|
|
403
561
|
|
|
404
562
|
const config = {
|
|
@@ -416,25 +574,11 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
416
574
|
if (ref !== this._component) {
|
|
417
575
|
this._component = ref;
|
|
418
576
|
}
|
|
419
|
-
|
|
420
|
-
// TODO: Delete this after React Native also deletes this deprecation helper.
|
|
421
|
-
if (ref != null && ref.getNode == null) {
|
|
422
|
-
ref.getNode = () => {
|
|
423
|
-
console.warn(
|
|
424
|
-
'%s: Calling %s on the ref of an Animated component ' +
|
|
425
|
-
'is no longer necessary. You can now directly use the ref ' +
|
|
426
|
-
'instead. This method will be removed in a future release.',
|
|
427
|
-
ref.constructor.name ?? '<<anonymous>>',
|
|
428
|
-
'getNode()'
|
|
429
|
-
);
|
|
430
|
-
return ref;
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
577
|
},
|
|
434
578
|
});
|
|
435
579
|
|
|
436
|
-
_filterNonAnimatedStyle(inputStyle) {
|
|
437
|
-
const style = {};
|
|
580
|
+
_filterNonAnimatedStyle(inputStyle: StyleProps) {
|
|
581
|
+
const style: StyleProps = {};
|
|
438
582
|
for (const key in inputStyle) {
|
|
439
583
|
const value = inputStyle[key];
|
|
440
584
|
if (!hasAnimatedNodes(value)) {
|
|
@@ -448,20 +592,23 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
448
592
|
return style;
|
|
449
593
|
}
|
|
450
594
|
|
|
451
|
-
_filterNonAnimatedProps(
|
|
452
|
-
|
|
595
|
+
_filterNonAnimatedProps(
|
|
596
|
+
inputProps: AnimatedComponentProps<InitialComponentProps>
|
|
597
|
+
): Record<string, unknown> {
|
|
598
|
+
const props: Record<string, unknown> = {};
|
|
453
599
|
for (const key in inputProps) {
|
|
454
600
|
const value = inputProps[key];
|
|
455
601
|
if (key === 'style') {
|
|
456
|
-
const
|
|
457
|
-
const
|
|
602
|
+
const styleProp = inputProps.style;
|
|
603
|
+
const styles = flattenArray<StyleProps>(styleProp ?? []);
|
|
604
|
+
const processedStyle: StyleProps = styles.map((style) => {
|
|
458
605
|
if (style && style.viewDescriptors) {
|
|
459
606
|
// this is how we recognize styles returned by useAnimatedStyle
|
|
460
607
|
style.viewsRef.add(this);
|
|
461
608
|
if (this._isFirstRender) {
|
|
462
609
|
return {
|
|
463
610
|
...style.initial.value,
|
|
464
|
-
...initialUpdaterRun(style.initial.updater),
|
|
611
|
+
...initialUpdaterRun<StyleProps>(style.initial.updater),
|
|
465
612
|
};
|
|
466
613
|
} else {
|
|
467
614
|
return style.initial.value;
|
|
@@ -474,10 +621,15 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
474
621
|
StyleSheet.flatten(processedStyle)
|
|
475
622
|
);
|
|
476
623
|
} else if (key === 'animatedProps') {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
624
|
+
const animatedProp = inputProps.animatedProps as Partial<
|
|
625
|
+
AnimatedComponentProps<AnimatedProps>
|
|
626
|
+
>;
|
|
627
|
+
if (animatedProp.initial !== undefined) {
|
|
628
|
+
Object.keys(animatedProp.initial.value).forEach((key) => {
|
|
629
|
+
props[key] = animatedProp.initial?.value[key];
|
|
630
|
+
animatedProp.viewsRef?.add(this);
|
|
631
|
+
});
|
|
632
|
+
}
|
|
481
633
|
} else if (value instanceof AnimatedEvent) {
|
|
482
634
|
// we cannot filter out event listeners completely as some components
|
|
483
635
|
// rely on having a callback registered in order to generate events
|
|
@@ -485,13 +637,15 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
485
637
|
// native event dispatcher to hijack events.
|
|
486
638
|
props[key] = dummyListener;
|
|
487
639
|
} else if (
|
|
488
|
-
|
|
640
|
+
has('current', value) &&
|
|
489
641
|
value.current instanceof WorkletEventHandler
|
|
490
642
|
) {
|
|
491
643
|
if (value.current.eventNames.length > 0) {
|
|
492
644
|
value.current.eventNames.forEach((eventName) => {
|
|
493
|
-
props[eventName] = value.current
|
|
494
|
-
? value.current.listeners[
|
|
645
|
+
props[eventName] = has('listeners', value.current)
|
|
646
|
+
? (value.current.listeners as Record<string, unknown>)[
|
|
647
|
+
eventName
|
|
648
|
+
]
|
|
495
649
|
: dummyListener;
|
|
496
650
|
});
|
|
497
651
|
} else {
|
|
@@ -504,7 +658,7 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
504
658
|
} else if (value instanceof AnimatedValue) {
|
|
505
659
|
// if any prop in animated component is set directly to the `Value` we set those props to the first value of `Value` node in order
|
|
506
660
|
// to avoid default values for a short moment when `Value` is being asynchrounously sent via bridge and initialized in the native side.
|
|
507
|
-
props[key] = value._startingValue;
|
|
661
|
+
props[key] = (value as AnimatedValue)._startingValue;
|
|
508
662
|
}
|
|
509
663
|
}
|
|
510
664
|
return props;
|
|
@@ -534,11 +688,11 @@ export default function createAnimatedComponent(Component, options = {}) {
|
|
|
534
688
|
Component.displayName || Component.name || 'Component'
|
|
535
689
|
})`;
|
|
536
690
|
|
|
537
|
-
return React.forwardRef(
|
|
691
|
+
return React.forwardRef<Component>((props, ref) => {
|
|
538
692
|
return (
|
|
539
693
|
<AnimatedComponent
|
|
540
694
|
{...props}
|
|
541
|
-
{...(ref
|
|
695
|
+
{...(ref === null ? null : { forwardedRef: ref })}
|
|
542
696
|
/>
|
|
543
697
|
);
|
|
544
698
|
});
|