14
Thenable,
15
RejectedThenable,
16
Awaited,
17
+ StartGesture,
18
+ GestureProvider,
19
} from 'shared/ReactTypes';
20
import type {
21
Fiber,
28
import type {HookFlags} from './ReactHookEffectTags';
29
import type {Flags} from './ReactFiberFlags';
30
import type {TransitionStatus} from './ReactFiberConfig';
31
+import type {ScheduledGesture} from './ReactFiberGestureScheduler';
32
33
import {
34
HostTransitionContext,
45
enableLegacyCache,
46
disableLegacyMode,
47
enableNoCloningMemoCache,
48
+ enableSwipeTransition,
49
} from 'shared/ReactFeatureFlags';
50
import {
51
REACT_CONTEXT_TYPE,
74
isTransitionLane,
75
markRootEntangled,
76
includesSomeLane,
77
+ isGestureRender,
78
+ GestureLane,
79
} from './ReactFiberLane';
80
import {
81
ContinuousEventPriority,
136
enqueueConcurrentHookUpdate,
137
enqueueConcurrentHookUpdateAndEagerlyBailout,
138
enqueueConcurrentRenderForLane,
139
+ enqueueGestureRender,
140
} from './ReactFiberConcurrentUpdates';
141
import {getTreeId} from './ReactFiberTreeContext';
142
import {now} from './Scheduler';
160
161
import {callComponentInDEV} from './ReactFiberCallUserSpace';
162
163
+import {
164
+ scheduleGesture,
165
+ cancelScheduledGesture,
166
+} from './ReactFiberGestureScheduler';
167
+
168
export type Update<S, A> = {
169
lane: Lane,
170
revertLane: Lane,
3972
}
3973
}
3974
3975
+type SwipeTransitionGestureUpdate = {
3976
+ gesture: ScheduledGesture,
3977
+ prev: SwipeTransitionGestureUpdate | null,
3978
+ next: SwipeTransitionGestureUpdate | null,
3979
+};
3980
+
3981
+type SwipeTransitionUpdateQueue = {
3982
+ pending: null | SwipeTransitionGestureUpdate,
3983
+ dispatch: StartGesture,
3984
+};
3985
+
3986
+function startGesture(
3987
+ fiber: Fiber,
3988
+ queue: SwipeTransitionUpdateQueue,
3989
+ gestureProvider: GestureProvider,
3990
+): () => void {
3991
+ const root = enqueueGestureRender(fiber);
3992
+ if (root === null) {
3993
+ // Already unmounted.
3994
+ // TODO: Should we warn here about starting on an unmounted Fiber?
3995
+ return function cancelGesture() {
3996
+ // Noop.
3997
+ };
3998
+ }
3999
+ const scheduledGesture = scheduleGesture(root, gestureProvider);
4000
+ // Add this particular instance to the queue.
4001
+ // We add multiple of the same provider even if they get batched so
4002
+ // that if we cancel one but not the other we can keep track of this.
4003
+ // Order doesn't matter but we insert in the beginning to avoid two fields.
4004
+ const update: SwipeTransitionGestureUpdate = {
4005
+ gesture: scheduledGesture,
4006
+ prev: null,
4007
+ next: queue.pending,
4008
+ };
4009
+ if (queue.pending !== null) {
4010
+ queue.pending.prev = update;
4011
+ }
4012
+ queue.pending = update;
4013
+ return function cancelGesture(): void {
4014
+ if (update.prev === null) {
4015
+ if (queue.pending === update) {
4016
+ queue.pending = update.next;
4017
+ } else {
4018
+ // This was already cancelled. Avoid double decrementing if someone calls this twice by accident.
4019
+ // TODO: Should we warn here about double cancelling?
4020
+ return;
4021
+ }
4022
+ } else {
4023
+ update.prev.next = update.next;
4024
+ if (update.next !== null) {
4025
+ update.next.prev = update.prev;
4026
+ }
4027
+ update.prev = null;
4028
+ update.next = null;
4029
+ }
4030
+ const cancelledGestured = update.gesture;
4031
+ // Decrement ref count of the root schedule.
4032
+ cancelScheduledGesture(root, cancelledGestured);
4033
+ };
4034
+}
4035
+
4036
+function mountSwipeTransition<T>(
4037
+ previous: T,
4038
+ current: T,
4039
+ next: T,
4040
+): [T, StartGesture] {
4041
+ const queue: SwipeTransitionUpdateQueue = {
4042
+ pending: null,
4043
+ dispatch: (null: any),
4044
+ };
4045
+ const startGestureOnHook: StartGesture = (queue.dispatch = (startGesture.bind(
4046
+ null,
4047
+ currentlyRenderingFiber,
4048
+ queue,
4049
+ ): any));
4050
+ const hook = mountWorkInProgressHook();
4051
+ hook.queue = queue;
4052
+ return [current, startGestureOnHook];
4053
+}
4054
+
4055
+function updateSwipeTransition<T>(
4056
+ previous: T,
4057
+ current: T,
4058
+ next: T,
4059
+): [T, StartGesture] {
4060
+ const hook = updateWorkInProgressHook();
4061
+ const queue: SwipeTransitionUpdateQueue = hook.queue;
4062
+ const startGestureOnHook: StartGesture = queue.dispatch;
4063
+ const rootRenderLanes = getWorkInProgressRootRenderLanes();
4064
+ let value = current;
4065
+ if (isGestureRender(rootRenderLanes)) {
4066
+ // We're inside a gesture render. We'll traverse the queue to see if
4067
+ // this specific Hook is part of this gesture and, if so, which
4068
+ // direction to render.
4069
+ const root: FiberRoot | null = getWorkInProgressRoot();
4070
+ if (root === null) {
4071
+ throw new Error(
4072
+ 'Expected a work-in-progress root. This is a bug in React. Please file an issue.',
4073
+ );
4074
+ }
4075
+ // We assume that the currently rendering gesture is the one first in the queue.
4076
+ const rootRenderGesture = root.gestures;
4077
+ let update = queue.pending;
4078
+ while (update !== null) {
4079
+ if (rootRenderGesture === update.gesture) {
4080
+ // We had a match, meaning we're currently rendering a direction of this
4081
+ // hook for this gesture.
4082
+ // TODO: Determine which direction this gesture is currently rendering.
4083
+ value = previous;
4084
+ break;
4085
+ }
4086
+ update = update.next;
4087
+ }
4088
+ }
4089
+ if (queue.pending !== null) {
4090
+ // As long as there are any active gestures we need to leave the lane on
4091
+ // in case we need to render it later. Since a gesture render doesn't commit
4092
+ // the only time it really fully gets cleared is if something else rerenders
4093
+ // this component after all the active gestures has cleared.
4094
+ currentlyRenderingFiber.lanes = mergeLanes(
4095
+ currentlyRenderingFiber.lanes,
4096
+ GestureLane,
4097
+ );
4098
+ }
4099
+ return [value, startGestureOnHook];
4100
+}
4101
+
4102
export const ContextOnlyDispatcher: Dispatcher = {
4103
readContext,
4104
4128
if (enableUseEffectEventHook) {
4129
(ContextOnlyDispatcher: Dispatcher).useEffectEvent = throwInvalidHookError;
4130
}
4131
+if (enableSwipeTransition) {
4132
+ (ContextOnlyDispatcher: Dispatcher).useSwipeTransition =
4133
+ throwInvalidHookError;
4134
+}
4135
4136
const HooksDispatcherOnMount: Dispatcher = {
4137
readContext,
4162
if (enableUseEffectEventHook) {
4163
(HooksDispatcherOnMount: Dispatcher).useEffectEvent = mountEvent;
4164
}
4165
+if (enableSwipeTransition) {
4166
+ (HooksDispatcherOnMount: Dispatcher).useSwipeTransition =
4167
+ mountSwipeTransition;
4168
+}
4169
4170
const HooksDispatcherOnUpdate: Dispatcher = {
4171
readContext,
4196
if (enableUseEffectEventHook) {
4197
(HooksDispatcherOnUpdate: Dispatcher).useEffectEvent = updateEvent;
4198
}
4199
+if (enableSwipeTransition) {
4200
+ (HooksDispatcherOnUpdate: Dispatcher).useSwipeTransition =
4201
+ updateSwipeTransition;
4202
+}
4203
4204
const HooksDispatcherOnRerender: Dispatcher = {
4205
readContext,
4230
if (enableUseEffectEventHook) {
4231
(HooksDispatcherOnRerender: Dispatcher).useEffectEvent = updateEvent;
4232
}
4233
+if (enableSwipeTransition) {
4234
+ (HooksDispatcherOnRerender: Dispatcher).useSwipeTransition =
4235
+ updateSwipeTransition;
4236
+}
4237
4238
let HooksDispatcherOnMountInDEV: Dispatcher | null = null;
4239
let HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher | null = null;
4451
return mountEvent(callback);
4452
};
4453
}
4454
+ if (enableSwipeTransition) {
4455
+ (HooksDispatcherOnMountInDEV: Dispatcher).useSwipeTransition =
4456
+ function useSwipeTransition<T>(
4457
+ previous: T,
4458
+ current: T,
4459
+ next: T,
4460
+ ): [T, StartGesture] {
4461
+ currentHookNameInDev = 'useSwipeTransition';
4462
+ mountHookTypesDev();
4463
+ return mountSwipeTransition(previous, current, next);
4464
+ };
4465
+ }
4466
4467
HooksDispatcherOnMountWithHookTypesInDEV = {
4468
readContext<T>(context: ReactContext<T>): T {
4646
return mountEvent(callback);
4647
};
4648
}
4649
+ if (enableSwipeTransition) {
4650
+ (HooksDispatcherOnMountWithHookTypesInDEV: Dispatcher).useSwipeTransition =
4651
+ function useSwipeTransition<T>(
4652
+ previous: T,
4653
+ current: T,
4654
+ next: T,
4655
+ ): [T, StartGesture] {
4656
+ currentHookNameInDev = 'useSwipeTransition';
4657
+ updateHookTypesDev();
4658
+ return updateSwipeTransition(previous, current, next);
4659
+ };
4660
+ }
4661
4662
HooksDispatcherOnUpdateInDEV = {
4663
readContext<T>(context: ReactContext<T>): T {
4841
return updateEvent(callback);
4842
};
4843
}
4844
+ if (enableSwipeTransition) {
4845
+ (HooksDispatcherOnUpdateInDEV: Dispatcher).useSwipeTransition =
4846
+ function useSwipeTransition<T>(
4847
+ previous: T,
4848
+ current: T,
4849
+ next: T,
4850
+ ): [T, StartGesture] {
4851
+ currentHookNameInDev = 'useSwipeTransition';
4852
+ updateHookTypesDev();
4853
+ return updateSwipeTransition(previous, current, next);
4854
+ };
4855
+ }
4856
4857
HooksDispatcherOnRerenderInDEV = {
4858
readContext<T>(context: ReactContext<T>): T {
5036
return updateEvent(callback);
5037
};
5038
}
5039
+ if (enableSwipeTransition) {
5040
+ (HooksDispatcherOnRerenderInDEV: Dispatcher).useSwipeTransition =
5041
+ function useSwipeTransition<T>(
5042
+ previous: T,
5043
+ current: T,
5044
+ next: T,
5045
+ ): [T, StartGesture] {
5046
+ currentHookNameInDev = 'useSwipeTransition';
5047
+ updateHookTypesDev();
5048
+ return updateSwipeTransition(previous, current, next);
5049
+ };
5050
+ }
5051
5052
InvalidNestedHooksDispatcherOnMountInDEV = {
5053
readContext<T>(context: ReactContext<T>): T {
5256
return mountEvent(callback);
5257
};
5258
}
5259
+ if (enableSwipeTransition) {
5260
+ (InvalidNestedHooksDispatcherOnMountInDEV: Dispatcher).useSwipeTransition =
5261
+ function useSwipeTransition<T>(
5262
+ previous: T,
5263
+ current: T,
5264
+ next: T,
5265
+ ): [T, StartGesture] {
5266
+ currentHookNameInDev = 'useSwipeTransition';
5267
+ warnInvalidHookAccess();
5268
+ mountHookTypesDev();
5269
+ return mountSwipeTransition(previous, current, next);
5270
+ };
5271
+ }
5272
5273
InvalidNestedHooksDispatcherOnUpdateInDEV = {
5274
readContext<T>(context: ReactContext<T>): T {
5477
return updateEvent(callback);
5478
};
5479
}
5480
+ if (enableSwipeTransition) {
5481
+ (InvalidNestedHooksDispatcherOnUpdateInDEV: Dispatcher).useSwipeTransition =
5482
+ function useSwipeTransition<T>(
5483
+ previous: T,
5484
+ current: T,
5485
+ next: T,
5486
+ ): [T, StartGesture] {
5487
+ currentHookNameInDev = 'useSwipeTransition';
5488
+ warnInvalidHookAccess();
5489
+ updateHookTypesDev();
5490
+ return updateSwipeTransition(previous, current, next);
5491
+ };
5492
+ }
5493
5494
InvalidNestedHooksDispatcherOnRerenderInDEV = {
5495
readContext<T>(context: ReactContext<T>): T {
5698
return updateEvent(callback);
5699
};
5700
}
5701
+ if (enableSwipeTransition) {
5702
+ (InvalidNestedHooksDispatcherOnRerenderInDEV: Dispatcher).useSwipeTransition =
5703
+ function useSwipeTransition<T>(
5704
+ previous: T,
5705
+ current: T,
5706
+ next: T,
5707
+ ): [T, StartGesture] {
5708
+ currentHookNameInDev = 'useSwipeTransition';
5709
+ warnInvalidHookAccess();
5710
+ updateHookTypesDev();
5711
+ return updateSwipeTransition(previous, current, next);
5712
+ };
5713
+ }
5714
}