import type {FiberRoot} from './ReactInternalTypes';
import type {
GestureTimeline,
RunningGestureTransition,
} from './ReactFiberConfig';
import {
GestureLane,
includesBlockingLane,
includesTransitionLane,
} from './ReactFiberLane';
import {ensureRootIsScheduled} from './ReactFiberRootScheduler';
import {
subscribeToGestureDirection,
stopGestureTransition,
} from './ReactFiberConfig';
export type ScheduledGesture = {
provider: GestureTimeline,
count: number,
direction: boolean,
rangePrevious: number,
rangeCurrent: number,
rangeNext: number,
cancel: () => void,
running: null | RunningGestureTransition,
prev: null | ScheduledGesture,
next: null | ScheduledGesture,
};
export function scheduleGesture(
root: FiberRoot,
provider: GestureTimeline,
initialDirection: boolean,
rangePrevious: number,
rangeCurrent: number,
rangeNext: number,
): ScheduledGesture {
let prev = root.pendingGestures;
while (prev !== null) {
if (prev.provider === provider) {
prev.count++;
return prev;
}
const next = prev.next;
if (next === null) {
break;
}
prev = next;
}
const isFlippedDirection = rangePrevious > rangeNext;
const cancel = subscribeToGestureDirection(
provider,
rangeCurrent,
(direction: boolean) => {
if (isFlippedDirection) {
direction = !direction;
}
if (gesture.direction !== direction) {
gesture.direction = direction;
if (gesture.prev === null && root.pendingGestures !== gesture) {
const existing = root.pendingGestures;
gesture.next = existing;
if (existing !== null) {
existing.prev = gesture;
}
root.pendingGestures = gesture;
root.pendingLanes |= GestureLane;
ensureRootIsScheduled(root);
}
}
},
);
const gesture: ScheduledGesture = {
provider: provider,
count: 1,
direction: initialDirection,
rangePrevious: rangePrevious,
rangeCurrent: rangeCurrent,
rangeNext: rangeNext,
cancel: cancel,
running: null,
prev: prev,
next: null,
};
if (prev === null) {
root.pendingGestures = gesture;
} else {
prev.next = gesture;
}
ensureRootIsScheduled(root);
return gesture;
}
export function cancelScheduledGesture(
root: FiberRoot,
gesture: ScheduledGesture,
): void {
gesture.count--;
if (gesture.count === 0) {
const cancelDirectionSubscription = gesture.cancel;
cancelDirectionSubscription();
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;
stopGestureTransition(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) {
stopGestureTransition(gesture.running);
gesture.running = null;
}
const nextGesture = gesture.next;
gesture.next = null;
gesture.prev = null;
gesture = nextGesture;
}
}