import type {Fiber, FiberRoot} from './ReactInternalTypes';
import type {
Thenable,
GestureProvider,
GestureOptions,
} from 'shared/ReactTypes';
import {NoLane, type Lanes} from './ReactFiberLane';
import type {StackCursor} from './ReactFiberStack';
import type {Cache, SpawnedCachePool} from './ReactFiberCacheComponent';
import type {Transition} from 'react/src/ReactStartTransition';
import type {ScheduledGesture} from './ReactFiberGestureScheduler';
import {
enableTransitionTracing,
enableViewTransition,
enableGestureTransition,
} from 'shared/ReactFeatureFlags';
import {isPrimaryRenderer} from './ReactFiberConfig';
import {createCursor, push, pop} from './ReactFiberStack';
import {
getWorkInProgressRoot,
getWorkInProgressTransitions,
} from './ReactFiberWorkLoop';
import {
createCache,
retainCache,
CacheContext,
} from './ReactFiberCacheComponent';
import {
queueTransitionTypes,
entangleAsyncTransitionTypes,
entangledTransitionTypes,
} from './ReactFiberTransitionTypes';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {
entangleAsyncAction,
peekEntangledActionLane,
} from './ReactFiberAsyncAction';
import {startAsyncTransitionTimer} from './ReactProfilerTimer';
import {firstScheduledRoot} from './ReactFiberRootScheduler';
import {
startScheduledGesture,
cancelScheduledGesture,
} from './ReactFiberGestureScheduler';
export const NoTransition = null;
const prevOnStartTransitionFinish = ReactSharedInternals.S;
ReactSharedInternals.S = function onStartTransitionFinishForReconciler(
transition: Transition,
returnValue: mixed,
) {
if (
typeof returnValue === 'object' &&
returnValue !== null &&
typeof returnValue.then === 'function'
) {
startAsyncTransitionTimer();
const thenable: Thenable<mixed> = (returnValue: any);
entangleAsyncAction(transition, thenable);
}
if (enableViewTransition) {
if (entangledTransitionTypes !== null) {
let root = firstScheduledRoot;
while (root !== null) {
queueTransitionTypes(root, entangledTransitionTypes);
root = root.next;
}
}
const transitionTypes = transition.types;
if (transitionTypes !== null) {
let root = firstScheduledRoot;
while (root !== null) {
queueTransitionTypes(root, transitionTypes);
root = root.next;
}
if (peekEntangledActionLane() !== NoLane) {
entangleAsyncTransitionTypes(transitionTypes);
}
}
}
if (prevOnStartTransitionFinish !== null) {
prevOnStartTransitionFinish(transition, returnValue);
}
};
function chainGestureCancellation(
root: FiberRoot,
scheduledGesture: ScheduledGesture,
prevCancel: null | (() => void),
): () => void {
return function cancelGesture(): void {
if (scheduledGesture !== null) {
cancelScheduledGesture(root, scheduledGesture);
}
if (prevCancel !== null) {
prevCancel();
}
};
}
if (enableGestureTransition) {
const prevOnStartGestureTransitionFinish = ReactSharedInternals.G;
ReactSharedInternals.G = function onStartGestureTransitionFinishForReconciler(
transition: Transition,
provider: GestureProvider,
options: ?GestureOptions,
): () => void {
let cancel = null;
if (prevOnStartGestureTransitionFinish !== null) {
cancel = prevOnStartGestureTransitionFinish(
transition,
provider,
options,
);
}
let root = firstScheduledRoot;
while (root !== null) {
const scheduledGesture = startScheduledGesture(
root,
provider,
options,
transition.types,
);
if (scheduledGesture !== null) {
cancel = chainGestureCancellation(root, scheduledGesture, cancel);
}
root = root.next;
}
if (cancel !== null) {
return cancel;
}
return function cancelGesture(): void {
};
};
}
export function requestCurrentTransition(): Transition | null {
return ReactSharedInternals.T;
}
const resumedCache: StackCursor<Cache | null> = createCursor(null);
const transitionStack: StackCursor<Array<Transition> | null> =
createCursor(null);
function peekCacheFromPool(): Cache | null {
const cacheResumedFromPreviousRender = resumedCache.current;
if (cacheResumedFromPreviousRender !== null) {
return cacheResumedFromPreviousRender;
}
const root = (getWorkInProgressRoot(): any);
const cacheFromRootCachePool = root.pooledCache;
return cacheFromRootCachePool;
}
export function requestCacheFromPool(renderLanes: Lanes): Cache {
const cacheFromPool = peekCacheFromPool();
if (cacheFromPool !== null) {
return cacheFromPool;
}
const root = (getWorkInProgressRoot(): any);
const freshCache = createCache();
root.pooledCache = freshCache;
retainCache(freshCache);
if (freshCache !== null) {
root.pooledCacheLanes |= renderLanes;
}
return freshCache;
}
export function pushRootTransition(
workInProgress: Fiber,
root: FiberRoot,
renderLanes: Lanes,
) {
if (enableTransitionTracing) {
const rootTransitions = getWorkInProgressTransitions();
push(transitionStack, rootTransitions, workInProgress);
}
}
export function popRootTransition(
workInProgress: Fiber,
root: FiberRoot,
renderLanes: Lanes,
) {
if (enableTransitionTracing) {
pop(transitionStack, workInProgress);
}
}
export function pushTransition(
offscreenWorkInProgress: Fiber,
prevCachePool: SpawnedCachePool | null,
newTransitions: Array<Transition> | null,
): void {
if (prevCachePool === null) {
push(resumedCache, resumedCache.current, offscreenWorkInProgress);
} else {
push(resumedCache, prevCachePool.pool, offscreenWorkInProgress);
}
if (enableTransitionTracing) {
if (transitionStack.current === null) {
push(transitionStack, newTransitions, offscreenWorkInProgress);
} else if (newTransitions === null) {
push(transitionStack, transitionStack.current, offscreenWorkInProgress);
} else {
push(
transitionStack,
transitionStack.current.concat(newTransitions),
offscreenWorkInProgress,
);
}
}
}
export function popTransition(workInProgress: Fiber, current: Fiber | null) {
if (current !== null) {
if (enableTransitionTracing) {
pop(transitionStack, workInProgress);
}
pop(resumedCache, workInProgress);
}
}
export function getPendingTransitions(): Array<Transition> | null {
if (!enableTransitionTracing) {
return null;
}
return transitionStack.current;
}
export function getSuspendedCache(): SpawnedCachePool | null {
const cacheFromPool = peekCacheFromPool();
if (cacheFromPool === null) {
return null;
}
return {
parent: isPrimaryRenderer
? CacheContext._currentValue
: CacheContext._currentValue2,
pool: cacheFromPool,
};
}
export function getOffscreenDeferredCache(): SpawnedCachePool | null {
const cacheFromPool = peekCacheFromPool();
if (cacheFromPool === null) {
return null;
}
return {
parent: isPrimaryRenderer
? CacheContext._currentValue
: CacheContext._currentValue2,
pool: cacheFromPool,
};
}