21
TransitionAbort,
22
} from './ReactFiberTracingMarkerComponent';
23
import type {OffscreenInstance} from './ReactFiberActivityComponent';
24
-import type {Resource, ViewTransitionInstance} from './ReactFiberConfig';
24
+import type {
25
+ Resource,
26
+ ViewTransitionInstance,
27
+ RunningViewTransition,
28
+} from './ReactFiberConfig';
29
import type {RootState} from './ReactFiberRoot';
30
import {
31
getViewTransitionName,
106
trackSchedulerEvent,
107
startViewTransition,
108
startGestureTransition,
109
+ stopViewTransition,
110
createViewTransitionInstance,
111
} from './ReactFiberConfig';
112
670
let pendingEffectsRenderEndTime: number = -0; // Profiling-only
671
let pendingPassiveTransitions: Array<Transition> | null = null;
672
let pendingRecoverableErrors: null | Array<CapturedValue<mixed>> = null;
673
+let pendingViewTransition: null | RunningViewTransition = null;
674
let pendingViewTransitionEvents: Array<(types: Array<string>) => void> | null =
675
null;
676
let pendingTransitionTypes: null | TransitionTypes = null;
3509
}
3510
3511
pendingEffectsStatus = PENDING_MUTATION_PHASE;
3506
- const startedViewTransition =
3507
- enableViewTransition &&
3508
- willStartViewTransition &&
3509
- startViewTransition(
3512
+ if (enableViewTransition && willStartViewTransition) {
3513
+ pendingViewTransition = startViewTransition(
3514
root.containerInfo,
3515
pendingTransitionTypes,
3516
flushMutationEffects,
3520
flushPassiveEffects,
3521
reportViewTransitionError,
3522
);
3519
- if (!startedViewTransition) {
3523
+ } else {
3524
// Flush synchronously.
3525
flushMutationEffects();
3526
flushLayoutEffects();
3650
}
3651
pendingEffectsStatus = NO_PENDING_EFFECTS;
3652
3653
+ pendingViewTransition = null; // The view transition has now fully started.
3654
+
3655
// Tell Scheduler to yield at the end of the frame, so the browser has an
3656
// opportunity to paint.
3657
requestPaint();
3921
pendingTransitionTypes = null;
3922
pendingEffectsStatus = PENDING_GESTURE_MUTATION_PHASE;
3923
3918
- finishedGesture.running = startGestureTransition(
3924
+ pendingViewTransition = finishedGesture.running = startGestureTransition(
3925
root.containerInfo,
3926
finishedGesture.provider,
3927
finishedGesture.rangeCurrent,
3981
pendingFinishedWork = (null: any); // Clear for GC purposes.
3982
pendingEffectsLanes = NoLanes;
3983
3984
+ pendingViewTransition = null; // The view transition has now fully started.
3985
+
3986
const prevTransition = ReactSharedInternals.T;
3987
ReactSharedInternals.T = null;
3988
const previousPriority = getCurrentUpdatePriority();
4033
}
4034
}
4035
4036
+let didWarnAboutInterruptedViewTransitions = false;
4037
+
4038
export function flushPendingEffects(wasDelayedCommit?: boolean): boolean {
4039
// Returns whether passive effects were flushed.
4040
+ if (enableViewTransition && pendingViewTransition !== null) {
4041
+ // If we forced a flush before the View Transition full started then we skip it.
4042
+ // This ensures that we're not running a partial animation.
4043
+ stopViewTransition(pendingViewTransition);
4044
+ if (__DEV__) {
4045
+ if (!didWarnAboutInterruptedViewTransitions) {
4046
+ didWarnAboutInterruptedViewTransitions = true;
4047
+ console.warn(
4048
+ 'A flushSync update cancelled a View Transition because it was called ' +
4049
+ 'while the View Transition was still preparing. To preserve the synchronous ' +
4050
+ 'semantics, React had to skip the View Transition. If you can, try to avoid ' +
4051
+ "flushSync() in a scenario that's likely to interfere.",
4052
+ );
4053
+ }
4054
+ }
4055
+ pendingViewTransition = null;
4056
+ }
4057
flushGestureMutations();
4058
flushGestureAnimations();
4059
flushMutationEffects();