100
resolveUpdatePriority,
101
trackSchedulerEvent,
102
startViewTransition,
103
+ startGestureTransition,
104
createViewTransitionInstance,
105
} from './ReactFiberConfig';
106
229
accumulateSuspenseyCommit,
230
} from './ReactFiberCommitWork';
231
import {shouldStartViewTransition} from './ReactFiberCommitViewTransitions';
232
+import {
233
+ insertDestinationClones,
234
+ applyDepartureTransitions,
235
+ startGestureAnimations,
236
+} from './ReactFiberApplyGesture';
237
import {enqueueUpdate} from './ReactFiberClassUpdateQueue';
238
import {resetContextDependencies} from './ReactFiberNewContext';
239
import {
347
import {getMaskedContext, getUnmaskedContext} from './ReactFiberContext';
348
import {peekEntangledActionLane} from './ReactFiberAsyncAction';
349
import {logUncaughtError} from './ReactFiberErrorLogger';
344
-import {deleteScheduledGesture} from './ReactFiberGestureScheduler';
350
+import {
351
+ deleteScheduledGesture,
352
+ stopCompletedGestures,
353
+} from './ReactFiberGestureScheduler';
354
355
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
356
653
const PENDING_AFTER_MUTATION_PHASE = 3;
654
const PENDING_SPAWNED_WORK = 4;
655
const PENDING_PASSIVE_PHASE = 5;
647
-let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 = 0;
656
+const PENDING_GESTURE_MUTATION_PHASE = 6;
657
+const PENDING_GESTURE_ANIMATION_PHASE = 7;
658
+let pendingEffectsStatus: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
659
let pendingEffectsRoot: FiberRoot = (null: any);
660
let pendingFinishedWork: Fiber = (null: any);
661
let pendingEffectsLanes: Lanes = NoLanes;
1435
const subtreeFlags = finishedWork.subtreeFlags;
1436
const isViewTransitionEligible =
1437
enableViewTransition && includesOnlyViewTransitionEligibleLanes(lanes); // TODO: Use a subtreeFlag to optimize.
1438
+ const isGestureTransition = enableSwipeTransition && isGestureRender(lanes);
1439
const maySuspendCommit =
1440
subtreeFlags & ShouldSuspendCommit ||
1441
(subtreeFlags & BothVisibilityAndMaySuspendCommit) ===
1442
BothVisibilityAndMaySuspendCommit;
1431
- if (isViewTransitionEligible || maySuspendCommit) {
1443
+ if (isViewTransitionEligible || maySuspendCommit || isGestureTransition) {
1444
// Before committing, ask the renderer whether the host tree is ready.
1445
// If it's not, we'll wait until it notifies us.
1446
startSuspendingCommit();
1451
// This will also track any newly added or appearing ViewTransition
1452
// components for the purposes of forming pairs.
1453
accumulateSuspenseyCommit(finishedWork);
1442
- if (isViewTransitionEligible) {
1443
- suspendOnActiveViewTransition(root.containerInfo);
1454
+ if (isViewTransitionEligible || isGestureTransition) {
1455
+ // If we're stopping gestures we don't have to wait for any pending
1456
+ // view transition. We'll stop it when we commit.
1457
+ if (!enableSwipeTransition || root.stoppingGestures === null) {
1458
+ suspendOnActiveViewTransition(root.containerInfo);
1459
+ }
1460
}
1461
// At the end, ask the renderer if it's ready to commit, or if we should
1462
// suspend. If it's not ready, it will return a callback to subscribe to
3279
if (enableSchedulingProfiler) {
3280
markCommitStopped();
3281
}
3282
+ if (enableSwipeTransition) {
3283
+ // Stop any gestures that were completed and is now being reverted.
3284
+ if (root.stoppingGestures !== null) {
3285
+ stopCompletedGestures(root);
3286
+ }
3287
+ }
3288
return;
3289
} else {
3290
if (__DEV__) {
3313
const concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
3314
remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
3315
3294
- if (enableSwipeTransition && root.gestures === null) {
3316
+ if (enableSwipeTransition && root.pendingGestures === null) {
3317
// Gestures don't clear their lanes while the gesture is still active but it
3318
// might not be scheduled to do any more renders and so we shouldn't schedule
3319
// any more gesture lane work until a new gesture is scheduled.
3343
// times out.
3344
}
3345
3324
- if (enableSwipeTransition && isGestureRender(lanes)) {
3325
- // This is a special kind of render that doesn't commit regular effects.
3326
- commitGestureOnRoot(
3327
- root,
3328
- finishedWork,
3329
- recoverableErrors,
3330
- enableProfilerTimer
3331
- ? suspendedCommitReason === IMMEDIATE_COMMIT
3332
- ? completedRenderEndTime
3333
- : commitStartTime
3334
- : 0,
3335
- );
3336
- return;
3337
- }
3338
-
3346
// workInProgressX might be overwritten, so we want
3347
// to store it in pendingPassiveX until they get processed
3348
// We need to pass this through as an argument to commitRoot
3361
pendingSuspendedCommitReason = suspendedCommitReason;
3362
}
3363
3364
+ if (enableSwipeTransition && isGestureRender(lanes)) {
3365
+ // This is a special kind of render that doesn't commit regular effects.
3366
+ commitGestureOnRoot(
3367
+ root,
3368
+ finishedWork,
3369
+ recoverableErrors,
3370
+ enableProfilerTimer
3371
+ ? suspendedCommitReason === IMMEDIATE_COMMIT
3372
+ ? completedRenderEndTime
3373
+ : commitStartTime
3374
+ : 0,
3375
+ );
3376
+ return;
3377
+ }
3378
+
3379
// If there are pending passive effects, schedule a callback to process them.
3380
// Do this as early as possible, so it is queued before anything else that
3381
// might get scheduled in the commit phase. (See #16714.)
3483
ReactSharedInternals.T = prevTransition;
3484
}
3485
}
3486
+
3487
+ let willStartViewTransition = shouldStartViewTransition;
3488
+ if (enableSwipeTransition) {
3489
+ // Stop any gestures that were completed and is now being committed.
3490
+ if (root.stoppingGestures !== null) {
3491
+ stopCompletedGestures(root);
3492
+ // If we are in the process of stopping some gesture we shouldn't start
3493
+ // a View Transition because that would start from the previous state to
3494
+ // the next state.
3495
+ willStartViewTransition = false;
3496
+ }
3497
+ }
3498
+
3499
pendingEffectsStatus = PENDING_MUTATION_PHASE;
3500
const startedViewTransition =
3501
enableViewTransition &&
3467
- shouldStartViewTransition &&
3502
+ willStartViewTransition &&
3503
startViewTransition(
3504
root.containerInfo,
3505
pendingTransitionTypes,
3668
} else {
3669
pendingEffectsStatus = NO_PENDING_EFFECTS;
3670
pendingEffectsRoot = (null: any); // Clear for GC purposes.
3671
+ pendingFinishedWork = (null: any); // Clear for GC purposes.
3672
// There were no passive effects, so we can immediately release the cache
3673
// pool for this render.
3674
releaseRootPooledCache(root, root.pendingLanes);
3866
3867
function commitGestureOnRoot(
3868
root: FiberRoot,
3833
- finishedWork: null | Fiber,
3869
+ finishedWork: Fiber,
3870
recoverableErrors: null | Array<CapturedValue<mixed>>,
3871
renderEndTime: number, // Profiling-only
3872
): void {
3873
// We assume that the gesture we just rendered was the first one in the queue.
3838
- const finishedGesture = root.gestures;
3874
+ const finishedGesture = root.pendingGestures;
3875
if (finishedGesture === null) {
3840
- throw new Error(
3841
- 'Finished rendering the gesture lane but there were no pending gestures. ' +
3842
- 'React should not have started a render in this case. This is a bug in React.',
3843
- );
3876
+ // We must have already cancelled this gesture before we had a chance to
3877
+ // render it. Let's schedule work on the next set of lanes.
3878
+ ensureRootIsScheduled(root);
3879
+ return;
3880
}
3881
deleteScheduledGesture(root, finishedGesture);
3846
- // TODO: Run the gesture
3882
+
3883
+ const prevTransition = ReactSharedInternals.T;
3884
+ ReactSharedInternals.T = null;
3885
+ const previousPriority = getCurrentUpdatePriority();
3886
+ setCurrentUpdatePriority(DiscreteEventPriority);
3887
+ const prevExecutionContext = executionContext;
3888
+ executionContext |= CommitContext;
3889
+ try {
3890
+ insertDestinationClones(root, finishedWork);
3891
+ } finally {
3892
+ // Reset the priority to the previous non-sync value.
3893
+ executionContext = prevExecutionContext;
3894
+ setCurrentUpdatePriority(previousPriority);
3895
+ ReactSharedInternals.T = prevTransition;
3896
+ }
3897
+ // TODO: Collect transition types.
3898
+ pendingTransitionTypes = null;
3899
+ pendingEffectsStatus = PENDING_GESTURE_MUTATION_PHASE;
3900
+
3901
+ finishedGesture.running = startGestureTransition(
3902
+ root.containerInfo,
3903
+ pendingTransitionTypes,
3904
+ flushGestureMutations,
3905
+ flushGestureAnimations,
3906
+ );
3907
+}
3908
+
3909
+function flushGestureMutations(): void {
3910
+ if (pendingEffectsStatus !== PENDING_GESTURE_MUTATION_PHASE) {
3911
+ return;
3912
+ }
3913
+ pendingEffectsStatus = NO_PENDING_EFFECTS;
3914
+ const root = pendingEffectsRoot;
3915
+ const finishedWork = pendingFinishedWork;
3916
+
3917
+ const prevTransition = ReactSharedInternals.T;
3918
+ ReactSharedInternals.T = null;
3919
+ const previousPriority = getCurrentUpdatePriority();
3920
+ setCurrentUpdatePriority(DiscreteEventPriority);
3921
+ const prevExecutionContext = executionContext;
3922
+ executionContext |= CommitContext;
3923
+ try {
3924
+ applyDepartureTransitions(root, finishedWork);
3925
+ } finally {
3926
+ // Reset the priority to the previous non-sync value.
3927
+ executionContext = prevExecutionContext;
3928
+ setCurrentUpdatePriority(previousPriority);
3929
+ ReactSharedInternals.T = prevTransition;
3930
+ }
3931
+
3932
+ pendingEffectsStatus = PENDING_GESTURE_ANIMATION_PHASE;
3933
+}
3934
+
3935
+function flushGestureAnimations(): void {
3936
+ // If we get canceled before we start we might not have applied
3937
+ // mutations yet. We need to apply them first.
3938
+ flushGestureMutations();
3939
+ if (pendingEffectsStatus !== PENDING_GESTURE_ANIMATION_PHASE) {
3940
+ return;
3941
+ }
3942
+ pendingEffectsStatus = NO_PENDING_EFFECTS;
3943
+ const root = pendingEffectsRoot;
3944
+ const finishedWork = pendingFinishedWork;
3945
+ pendingEffectsRoot = (null: any); // Clear for GC purposes.
3946
+ pendingFinishedWork = (null: any); // Clear for GC purposes.
3947
+ pendingEffectsLanes = NoLanes;
3948
+
3949
+ const prevTransition = ReactSharedInternals.T;
3950
+ ReactSharedInternals.T = null;
3951
+ const previousPriority = getCurrentUpdatePriority();
3952
+ setCurrentUpdatePriority(DiscreteEventPriority);
3953
+ const prevExecutionContext = executionContext;
3954
+ executionContext |= CommitContext;
3955
+ try {
3956
+ startGestureAnimations(root, finishedWork);
3957
+ } finally {
3958
+ // Reset the priority to the previous non-sync value.
3959
+ executionContext = prevExecutionContext;
3960
+ setCurrentUpdatePriority(previousPriority);
3961
+ ReactSharedInternals.T = prevTransition;
3962
+ }
3963
+
3964
+ // Now that we've rendered this lane. Start working on the next lane.
3965
+ ensureRootIsScheduled(root);
3966
}
3967
3968
function makeErrorInfo(componentStack: ?string) {
3998
3999
export function flushPendingEffects(wasDelayedCommit?: boolean): boolean {
4000
// Returns whether passive effects were flushed.
4001
+ flushGestureMutations();
4002
+ flushGestureAnimations();
4003
flushMutationEffects();
4004
flushLayoutEffects();
4005
// Skip flushAfterMutation if we're forcing this early.
4053
const lanes = pendingEffectsLanes;
4054
pendingEffectsStatus = NO_PENDING_EFFECTS;
4055
pendingEffectsRoot = (null: any); // Clear for GC purposes.
4056
+ pendingFinishedWork = (null: any); // Clear for GC purposes.
4057
// TODO: This is sometimes out of sync with pendingEffectsRoot.
4058
// Figure out why and fix it. It's not causing any known issues (probably
4059
// because it's only used for profiling), but it's a refactor hazard.