import type {FiberRoot} from './ReactInternalTypes';
import type {GestureOptions} from 'shared/ReactTypes';
import type {GestureTimeline, RunningViewTransition} from './ReactFiberConfig';
import type {TransitionTypes} from 'react/src/ReactTransitionType';
import {
GestureLane,
includesBlockingLane,
includesTransitionLane,
} from './ReactFiberLane';
import {ensureRootIsScheduled} from './ReactFiberRootScheduler';
import {getCurrentGestureOffset, stopViewTransition} from './ReactFiberConfig';
export type ScheduledGesture = {
provider: GestureTimeline,
count: number,
rangeStart: number,
rangeEnd: number,
types: null | TransitionTypes,
running: null | RunningViewTransition,
prev: null | ScheduledGesture,
next: null | ScheduledGesture,
};
export function scheduleGesture(
root: FiberRoot,
provider: GestureTimeline,
): ScheduledGesture {
let prev = root.pendingGestures;
while (prev !== null) {
if (prev.provider === provider) {
return prev;
}
const next = prev.next;
if (next === null) {
break;
}
prev = next;
}
const gesture: ScheduledGesture = {
provider: provider,
count: 0,
rangeStart: 0,
rangeEnd: 100,
types: null,
running: null,
prev: prev,
next: null,
};
if (prev === null) {
root.pendingGestures = gesture;
} else {
prev.next = gesture;
}
ensureRootIsScheduled(root);
return gesture;
}
export function startScheduledGesture(
root: FiberRoot,
gestureTimeline: GestureTimeline,
gestureOptions: ?GestureOptions,
transitionTypes: null | TransitionTypes,
): null | ScheduledGesture {
const rangeStart =
gestureOptions && gestureOptions.rangeStart != null
? gestureOptions.rangeStart
: getCurrentGestureOffset(gestureTimeline);
const rangeEnd =
gestureOptions && gestureOptions.rangeEnd != null
? gestureOptions.rangeEnd
: rangeStart < 50
? 100
: 0;
let prev = root.pendingGestures;
while (prev !== null) {
if (prev.provider === gestureTimeline) {
prev.count++;
prev.rangeStart = rangeStart;
prev.rangeEnd = rangeEnd;
if (transitionTypes !== null) {
let scheduledTypes = prev.types;
if (scheduledTypes === null) {
scheduledTypes = prev.types = [];
}
for (let i = 0; i < transitionTypes.length; i++) {
const transitionType = transitionTypes[i];
if (scheduledTypes.indexOf(transitionType) === -1) {
scheduledTypes.push(transitionType);
}
}
}
return prev;
}
const next = prev.next;
if (next === null) {
break;
}
prev = next;
}
return null;
}
export function cancelScheduledGesture(
root: FiberRoot,
gesture: ScheduledGesture,
): void {
gesture.count--;
if (gesture.count === 0) {
deleteScheduledGesture(root, gesture);
const runningTransition = gesture.running;
if (runningTransition !== null) {
const pendingLanesExcludingGestureLane = root.pendingLanes & ~GestureLane;
if (
includesBlockingLane(pendingLanesExcludingGestureLane) ||
includesTransitionLane(pendingLanesExcludingGestureLane)
) {
const existing = root.stoppingGestures;
if (existing !== null) {
gesture.next = existing;
existing.prev = gesture;
}
root.stoppingGestures = gesture;
} else {
gesture.running = null;
stopViewTransition(runningTransition);
}
}
}
}
export function deleteScheduledGesture(
root: FiberRoot,
gesture: ScheduledGesture,
): void {
if (gesture.prev === null) {
if (root.pendingGestures === gesture) {
root.pendingGestures = gesture.next;
if (root.pendingGestures === null) {
root.pendingLanes &= ~GestureLane;
}
}
if (root.stoppingGestures === gesture) {
root.stoppingGestures = gesture.next;
}
} else {
gesture.prev.next = gesture.next;
if (gesture.next !== null) {
gesture.next.prev = gesture.prev;
}
gesture.prev = null;
gesture.next = null;
}
}
export function stopCompletedGestures(root: FiberRoot) {
let gesture = root.stoppingGestures;
root.stoppingGestures = null;
while (gesture !== null) {
if (gesture.running !== null) {
stopViewTransition(gesture.running);
gesture.running = null;
}
const nextGesture = gesture.next;
gesture.next = null;
gesture.prev = null;
gesture = nextGesture;
}
}