Skip to content

Commit 2efe809

Browse files
Martin Boothfacebook-github-bot
authored andcommitted
Include offset as separate field in payloads (#50970)
Summary: Pull Request resolved: #50970 __onAnimatedValueUpdateReceived is called from 2 places. It is called from the onAnimatedValueUpdate subscription where the source of the values comes from here: https://fburl.com/8v2cwd2x with getValue() returning the offset + value components combined. It's also called from in the animation end callback here: https://fburl.com/h36xy2nw where the source of this value comes from https://fburl.com/sud7m7st. In this case it's accessing `nodeValue` directly rather than calling getValue() and so it only includes the value component. In this diff we pass both the value and offset in both onAnimatedValueUpdate callbacks as well as endCallback. This allows us to separate the value from the offset on the JS side and ensures we have the latest offset value from native Changelog: [Android][Fixed] - Sync offset and value from native -> js in separate fields Reviewed By: zeyap Differential Revision: D73795619 fbshipit-source-id: e8ed234497e3fcaf9d2a137aa1e17ca8d0f76d97
1 parent 1c5ec4b commit 2efe809

File tree

13 files changed

+72
-22
lines changed

13 files changed

+72
-22
lines changed

packages/react-native/Libraries/Animated/__tests__/AnimatedValue-test.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@ describe('AnimatedValue', () => {
1616
return new AnimatedValue(0, {useNativeDriver: true});
1717
}
1818

19-
function emitMockUpdate(node: AnimatedValue, mockValue: number): void {
19+
function emitMockUpdate(
20+
node: AnimatedValue,
21+
mockValue: number,
22+
mockOffset: number,
23+
): void {
2024
const nativeTag = node.__nativeTag;
2125
expect(nativeTag).not.toBe(undefined);
2226

2327
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
2428
tag: nativeTag,
2529
value: mockValue,
30+
offset: mockOffset,
2631
});
2732
}
2833

@@ -63,12 +68,12 @@ describe('AnimatedValue', () => {
6368
const nativeTag = node.__nativeTag;
6469
expect(nativeTag).not.toBe(undefined);
6570

66-
emitMockUpdate(node, 123);
71+
emitMockUpdate(node, 123, 50);
6772
expect(callback).toBeCalledTimes(1);
6873

6974
node.removeListener(id);
7075

71-
emitMockUpdate(node, 456);
76+
emitMockUpdate(node, 456, 60);
7277
expect(callback).toBeCalledTimes(1);
7378
});
7479

@@ -102,7 +107,7 @@ describe('AnimatedValue', () => {
102107
node.__attach();
103108

104109
node.addListener(callbackA);
105-
emitMockUpdate(node, 123);
110+
emitMockUpdate(node, 123, 50);
106111
expect(callbackA).toBeCalledTimes(1);
107112

108113
node.__detach();
@@ -112,7 +117,7 @@ describe('AnimatedValue', () => {
112117
node.__attach();
113118
node.addListener(callbackB);
114119

115-
emitMockUpdate(node, 456);
120+
emitMockUpdate(node, 456, 60);
116121
expect(callbackA).toBeCalledTimes(1);
117122
expect(callbackB).toBeCalledTimes(1);
118123
});
@@ -199,4 +204,28 @@ describe('AnimatedValue', () => {
199204
).toBeCalledTimes(0);
200205
});
201206
});
207+
208+
describe('when receiving an update event', () => {
209+
it('calls __onAnimatedValueUpdateReceived with value and offset', () => {
210+
const callback = jest.fn();
211+
const node = createNativeAnimatedValue();
212+
node.__attach();
213+
node.addListener(callback);
214+
215+
const nativeTag = node.__nativeTag;
216+
expect(nativeTag).not.toBe(undefined);
217+
218+
emitMockUpdate(node, 123, 50);
219+
220+
const spy = jest.spyOn(node, '__onAnimatedValueUpdateReceived');
221+
222+
const mockValue = 100;
223+
const mockOffset = 50;
224+
225+
emitMockUpdate(node, mockValue, mockOffset);
226+
227+
expect(spy).toHaveBeenCalledWith(mockValue, mockOffset);
228+
spy.mockRestore();
229+
});
230+
});
202231
});

packages/react-native/Libraries/Animated/animations/Animation.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ import type AnimatedValue from '../nodes/AnimatedValue';
1515
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
1616
import AnimatedProps from '../nodes/AnimatedProps';
1717

18-
export type EndResult = {finished: boolean, value?: number, ...};
18+
export type EndResult = {
19+
finished: boolean,
20+
value?: number,
21+
offset?: number,
22+
...
23+
};
1924
export type EndCallback = (result: EndResult) => void;
2025

2126
export type AnimationConfig = $ReadOnly<{
@@ -140,9 +145,9 @@ export default class Animation {
140145
// When using natively driven animations, once the animation completes,
141146
// we need to ensure that the JS side nodes are synced with the updated
142147
// values.
143-
const {value} = result;
148+
const {value, offset} = result;
144149
if (value != null) {
145-
animatedValue.__onAnimatedValueUpdateReceived(value);
150+
animatedValue.__onAnimatedValueUpdateReceived(value, offset);
146151

147152
if (this.__isLooping === true) {
148153
return;

packages/react-native/Libraries/Animated/nodes/AnimatedNode.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ export default class AnimatedNode {
112112
return this.#listeners.size > 0;
113113
}
114114

115-
__onAnimatedValueUpdateReceived(value: number): void {
116-
this.__callListeners(value);
115+
__onAnimatedValueUpdateReceived(value: number, offset: number): void {
116+
this.__callListeners(value + offset);
117117
}
118118

119119
__callListeners(value: number): void {

packages/react-native/Libraries/Animated/nodes/AnimatedValue.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export default class AnimatedValue extends AnimatedWithChildren {
168168
'onAnimatedValueUpdate',
169169
data => {
170170
if (data.tag === nativeTag) {
171-
this.__onAnimatedValueUpdateReceived(data.value);
171+
this.__onAnimatedValueUpdateReceived(data.value, data.offset);
172172
}
173173
},
174174
);
@@ -288,8 +288,11 @@ export default class AnimatedValue extends AnimatedWithChildren {
288288
}
289289
}
290290

291-
__onAnimatedValueUpdateReceived(value: number): void {
291+
__onAnimatedValueUpdateReceived(value: number, offset?: number): void {
292292
this._updateValue(value, false /*flush*/);
293+
if (offset != null) {
294+
this._offset = offset;
295+
}
293296
}
294297

295298
/**

packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,12 @@ declare export function fromBouncinessAndSpeed(
417417
`;
418418

419419
exports[`public API should not change unintentionally Libraries/Animated/animations/Animation.js 1`] = `
420-
"export type EndResult = { finished: boolean, value?: number, ... };
420+
"export type EndResult = {
421+
finished: boolean,
422+
value?: number,
423+
offset?: number,
424+
...
425+
};
421426
export type EndCallback = (result: EndResult) => void;
422427
export type AnimationConfig = $ReadOnly<{
423428
isInteraction?: boolean,

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ public abstract class com/facebook/react/animated/AnimatedNode {
444444
}
445445

446446
public abstract interface class com/facebook/react/animated/AnimatedNodeValueListener {
447-
public abstract fun onValueUpdate (D)V
447+
public abstract fun onValueUpdate (DD)V
448448
}
449449

450450
public final class com/facebook/react/animated/NativeAnimatedModule : com/facebook/fbreact/specs/NativeAnimatedModuleSpec, com/facebook/react/bridge/LifecycleEventListener, com/facebook/react/bridge/UIManagerListener {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/AnimatedNodeValueListener.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ package com.facebook.react.animated
99

1010
/** Interface used to listen to [ValueAnimatedNode] updates. */
1111
public fun interface AnimatedNodeValueListener {
12-
public fun onValueUpdate(value: Double)
12+
public fun onValueUpdate(value: Double, offset: Double)
1313
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,10 +542,11 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext?) :
542542
FLog.d(NAME, "queue startListeningToAnimatedNodeValue: $tag")
543543
}
544544

545-
val listener = AnimatedNodeValueListener { value ->
545+
val listener = AnimatedNodeValueListener { value, offset ->
546546
val onAnimatedValueData = Arguments.createMap()
547547
onAnimatedValueData.putInt("tag", tag)
548548
onAnimatedValueData.putDouble("value", value)
549+
onAnimatedValueData.putDouble("offset", offset)
549550

550551
val reactApplicationContext = reactApplicationContextIfActiveOrWarn
551552
reactApplicationContext?.emitDeviceEvent("onAnimatedValueUpdate", onAnimatedValueData)
@@ -976,10 +977,11 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext?) :
976977

977978
BatchExecutionOpCodes.OP_START_LISTENING_TO_ANIMATED_NODE_VALUE -> {
978979
val tag = opsAndArgs.getInt(i++)
979-
val listener = AnimatedNodeValueListener { value ->
980+
val listener = AnimatedNodeValueListener { value, offset ->
980981
val onAnimatedValueData = Arguments.createMap()
981982
onAnimatedValueData.putInt("tag", tag)
982983
onAnimatedValueData.putDouble("value", value)
984+
onAnimatedValueData.putDouble("offset", offset)
983985

984986
val reactApplicationContext = reactApplicationContextIfActiveOrWarn
985987
reactApplicationContext?.emitDeviceEvent(

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ public class NativeAnimatedNodesManager(
272272
val endCallbackResponse = Arguments.createMap()
273273
endCallbackResponse.putBoolean("finished", false)
274274
endCallbackResponse.putDouble("value", animatedValueNonnull.nodeValue)
275+
endCallbackResponse.putDouble("offset", animatedValueNonnull.offset)
275276
animation.endCallback?.invoke(endCallbackResponse)
276277
} else if (reactApplicationContext != null) {
277278
// If no callback is passed in, this /may/ be an animation set up by the single-op
@@ -281,6 +282,7 @@ public class NativeAnimatedNodesManager(
281282
params.putInt("animationId", animation.id)
282283
params.putBoolean("finished", false)
283284
params.putDouble("value", animatedValueNonnull.nodeValue)
285+
params.putDouble("offset", animatedValueNonnull.offset)
284286
events = events ?: Arguments.createArray()
285287
events.pushMap(params)
286288
}
@@ -309,6 +311,7 @@ public class NativeAnimatedNodesManager(
309311
val endCallbackResponse = Arguments.createMap()
310312
endCallbackResponse.putBoolean("finished", false)
311313
endCallbackResponse.putDouble("value", checkNotNull(animation.animatedValue).nodeValue)
314+
endCallbackResponse.putDouble("offset", checkNotNull(animation.animatedValue).offset)
312315
checkNotNull(animation.endCallback).invoke(endCallbackResponse)
313316
} else if (reactApplicationContext != null) {
314317
// If no callback is passed in, this /may/ be an animation set up by the single-op
@@ -318,6 +321,7 @@ public class NativeAnimatedNodesManager(
318321
params.putInt("animationId", animation.id)
319322
params.putBoolean("finished", false)
320323
params.putDouble("value", checkNotNull(animation.animatedValue).nodeValue)
324+
params.putDouble("offset", checkNotNull(animation.animatedValue).offset)
321325
events = events ?: Arguments.createArray()
322326
events.pushMap(params)
323327
}
@@ -578,6 +582,7 @@ public class NativeAnimatedNodesManager(
578582
val endCallbackResponse = Arguments.createMap()
579583
endCallbackResponse.putBoolean("finished", true)
580584
endCallbackResponse.putDouble("value", animatedValueNonnull.nodeValue)
585+
endCallbackResponse.putDouble("offset", animatedValueNonnull.offset)
581586
animation.endCallback?.invoke(endCallbackResponse)
582587
} else if (reactApplicationContext != null) {
583588
// If no callback is passed in, this /may/ be an animation set up by the single-op
@@ -587,6 +592,7 @@ public class NativeAnimatedNodesManager(
587592
params.putInt("animationId", animation.id)
588593
params.putBoolean("finished", true)
589594
params.putDouble("value", animatedValueNonnull.nodeValue)
595+
params.putDouble("offset", animatedValueNonnull.offset)
590596
events = events ?: Arguments.createArray()
591597
events.pushMap(params)
592598
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/ValueAnimatedNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal open class ValueAnimatedNode(config: ReadableMap? = null) : AnimatedNod
3838
}
3939

4040
fun onValueUpdate(): Unit {
41-
valueListener?.onValueUpdate(getValue())
41+
valueListener?.onValueUpdate(getValue() - offset, offset)
4242
}
4343

4444
fun setValueListener(listener: AnimatedNodeValueListener?): Unit {

0 commit comments

Comments
 (0)