react-native-smooth-clip-view 0.2.6 → 0.2.7
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/README.md +32 -18
- package/android/build.gradle +15 -0
- package/android/src/main/cpp/SmoothClipAndroid.h +7 -8
- package/android/src/main/cpp/SmoothClipBindings.cpp +11 -7
- package/android/src/main/cpp/SmoothClipRegistry.cpp +139 -137
- package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +8 -4
- package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +75 -8
- package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +1 -0
- package/cpp/SmoothClipAnimationCurve.h +92 -0
- package/cpp/SmoothClipVelocityTracker.h +5 -7
- package/ios/SmoothClipRegistry.mm +231 -132
- package/ios/SmoothClipView.mm +38 -150
- package/ios/SmoothClipViewRegistry.h +4 -1
- package/lib/module/driverState.js +8 -0
- package/lib/module/driverState.js.map +1 -1
- package/lib/module/drivers.native.js +27 -6
- package/lib/module/drivers.native.js.map +1 -1
- package/lib/module/reactRequests.js +9 -3
- package/lib/module/reactRequests.js.map +1 -1
- package/lib/typescript/src/driverState.d.ts.map +1 -1
- package/lib/typescript/src/drivers.native.d.ts.map +1 -1
- package/lib/typescript/src/reactRequests.d.ts +3 -1
- package/lib/typescript/src/reactRequests.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/driverState.ts +8 -0
- package/src/drivers.native.ts +31 -7
- package/src/reactRequests.ts +14 -2
package/src/driverState.ts
CHANGED
|
@@ -48,6 +48,14 @@ export function detachDriverState(state: DriverState): void {
|
|
|
48
48
|
if (statesById.get(state.driverId) === state) {
|
|
49
49
|
statesById.delete(state.driverId);
|
|
50
50
|
}
|
|
51
|
+
// Requests pending at teardown were settled by rejectDriverRequests, but
|
|
52
|
+
// their completion deferrals can no longer be released: the late-scheduled
|
|
53
|
+
// resolver's releaseDriverCompletions no-ops against a detached state. An
|
|
54
|
+
// effect replay reattaches THIS same object, so a stale positive count
|
|
55
|
+
// would queue every future completion forever. The deferrals died with
|
|
56
|
+
// their requests — reset them.
|
|
57
|
+
state.completionDeferrals = 0;
|
|
58
|
+
state.deferredCompletions.length = 0;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
export function createDriverState(
|
package/src/drivers.native.ts
CHANGED
|
@@ -107,9 +107,19 @@ function animationIsFinite(
|
|
|
107
107
|
return false;
|
|
108
108
|
}
|
|
109
109
|
if (animation.type === 'timing') {
|
|
110
|
+
const [x1, , x2] = animation.controlPoints;
|
|
110
111
|
return (
|
|
111
112
|
Number.isFinite(animation.duration) &&
|
|
112
|
-
animation.controlPoints.every(Number.isFinite)
|
|
113
|
+
animation.controlPoints.every(Number.isFinite) &&
|
|
114
|
+
// A cubic-bezier easing is only defined for x within [0,1] (the CSS /
|
|
115
|
+
// CoreAnimation / Reanimated Easing.bezier contract). Out-of-range x
|
|
116
|
+
// makes the Android parameter solve meaningless while CoreAnimation
|
|
117
|
+
// silently clamps — reject like any other invalid input instead of
|
|
118
|
+
// diverging per platform.
|
|
119
|
+
x1 >= 0 &&
|
|
120
|
+
x1 <= 1 &&
|
|
121
|
+
x2 >= 0 &&
|
|
122
|
+
x2 <= 1
|
|
113
123
|
);
|
|
114
124
|
}
|
|
115
125
|
if (animation.type === 'spring') {
|
|
@@ -322,6 +332,10 @@ export function useSmoothClipDriver(
|
|
|
322
332
|
animation: SmoothClipAnimation
|
|
323
333
|
): number => {
|
|
324
334
|
'worklet';
|
|
335
|
+
// Cleanup owns the terminal state. Check it before validation so even an
|
|
336
|
+
// invalid stale call cannot mint a native rejection id/completion against
|
|
337
|
+
// a driver whose listener and registry entry are already gone.
|
|
338
|
+
if (disposed.value !== 0) return 0;
|
|
325
339
|
if (
|
|
326
340
|
!isFiniteClipPresentation(target) ||
|
|
327
341
|
!animationIsFinite(target, animation)
|
|
@@ -334,8 +348,6 @@ export function useSmoothClipDriver(
|
|
|
334
348
|
// rejectAnimation, which would mint an id and a completion for a driver
|
|
335
349
|
// whose JS state is already detached. This is the "unsupported dispatch"
|
|
336
350
|
// arm of the documented 0 contract.
|
|
337
|
-
if (disposed.value !== 0) return 0;
|
|
338
|
-
|
|
339
351
|
const from = animation.from;
|
|
340
352
|
if (from !== undefined) {
|
|
341
353
|
// Fused hot write: exactly setScalars(from…) issued immediately
|
|
@@ -475,8 +487,10 @@ export function useSmoothClipDriver(
|
|
|
475
487
|
// dispatch. A missing driver is accepted when this call carries the
|
|
476
488
|
// authoritative interactive start above; a start-less missing-state
|
|
477
489
|
// request remains unsupported rather than inventing zero geometry.
|
|
478
|
-
// Validation failures never get here
|
|
479
|
-
// effect with a fresh id and one finished:false completion
|
|
490
|
+
// Validation failures never get here: they reject before any side
|
|
491
|
+
// effect, with a fresh id and one finished:false completion once the
|
|
492
|
+
// native entry exists — in the pre-seed window rejectAnimation has no
|
|
493
|
+
// entry and returns the bare 0 sentinel with no completion instead.
|
|
480
494
|
ownership.value = INTERACTIVE;
|
|
481
495
|
presentation.value = start;
|
|
482
496
|
if (scalarsWereStale) scalarsStale.value = 1;
|
|
@@ -568,7 +582,14 @@ export function useSmoothClipDriver(
|
|
|
568
582
|
return promise;
|
|
569
583
|
},
|
|
570
584
|
set(next) {
|
|
571
|
-
|
|
585
|
+
// Teardown resolves (undefined) rather than rejects: `set` is a
|
|
586
|
+
// documented fire-and-forget call, and a promise discarded with
|
|
587
|
+
// `void` must not become an unhandled rejection at unmount.
|
|
588
|
+
const { requestId, promise } = createReactRequest<void>(
|
|
589
|
+
driverId,
|
|
590
|
+
false,
|
|
591
|
+
{ value: undefined }
|
|
592
|
+
);
|
|
572
593
|
scheduleOnUI(() => {
|
|
573
594
|
'worklet';
|
|
574
595
|
setOnUI(next);
|
|
@@ -577,9 +598,12 @@ export function useSmoothClipDriver(
|
|
|
577
598
|
return promise;
|
|
578
599
|
},
|
|
579
600
|
animateTo(target, animation) {
|
|
601
|
+
// Teardown resolves with the documented 0 rejection sentinel (see
|
|
602
|
+
// `set` above): the README's own example voids this promise.
|
|
580
603
|
const { requestId, promise } = createReactRequest<number>(
|
|
581
604
|
driverId,
|
|
582
|
-
true
|
|
605
|
+
true,
|
|
606
|
+
{ value: 0 }
|
|
583
607
|
);
|
|
584
608
|
scheduleOnUI(() => {
|
|
585
609
|
'worklet';
|
package/src/reactRequests.ts
CHANGED
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
type PendingRequest = {
|
|
7
7
|
resolve(value: unknown): void;
|
|
8
8
|
reject(error: Error): void;
|
|
9
|
+
// Set for calls whose documented usage is fire-and-forget (`void
|
|
10
|
+
// driver.react.animateTo(...)`): teardown resolves them with this benign
|
|
11
|
+
// sentinel instead of rejecting, so a discarded promise cannot surface as
|
|
12
|
+
// an unhandled rejection during ordinary unmount.
|
|
13
|
+
teardownResolution?: { value: unknown };
|
|
9
14
|
};
|
|
10
15
|
|
|
11
16
|
const requests = new Map<number, PendingRequest>();
|
|
@@ -14,7 +19,8 @@ let nextRequestId = 0;
|
|
|
14
19
|
|
|
15
20
|
export function createReactRequest<T>(
|
|
16
21
|
driverId: number,
|
|
17
|
-
deferCompletions = false
|
|
22
|
+
deferCompletions = false,
|
|
23
|
+
teardownResolution?: { value: unknown }
|
|
18
24
|
): { requestId: number; promise: Promise<T> } {
|
|
19
25
|
nextRequestId = (nextRequestId % 0x7ffffffe) + 1;
|
|
20
26
|
const requestId = nextRequestId;
|
|
@@ -27,6 +33,7 @@ export function createReactRequest<T>(
|
|
|
27
33
|
requests.set(requestId, {
|
|
28
34
|
resolve: resolveRequest as (value: unknown) => void,
|
|
29
35
|
reject: rejectRequest,
|
|
36
|
+
teardownResolution,
|
|
30
37
|
});
|
|
31
38
|
const driverRequests = requestsByDriver.get(driverId) ?? new Set<number>();
|
|
32
39
|
driverRequests.add(requestId);
|
|
@@ -58,7 +65,12 @@ export function rejectDriverRequests(driverId: number): void {
|
|
|
58
65
|
for (const requestId of driverRequests) {
|
|
59
66
|
const request = requests.get(requestId);
|
|
60
67
|
requests.delete(requestId);
|
|
61
|
-
|
|
68
|
+
if (!request) continue;
|
|
69
|
+
if (request.teardownResolution) {
|
|
70
|
+
request.resolve(request.teardownResolution.value);
|
|
71
|
+
} else {
|
|
72
|
+
request.reject(new Error('[SmoothClipView] Driver was destroyed.'));
|
|
73
|
+
}
|
|
62
74
|
}
|
|
63
75
|
requestsByDriver.delete(driverId);
|
|
64
76
|
}
|