mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-24 15:36:59 +01:00
222 lines
8.1 KiB
TypeScript
222 lines
8.1 KiB
TypeScript
import Animated, { clockRunning, not, startClock, stopClock } from 'react-native-reanimated';
|
|
import { isWeb } from './constants';
|
|
|
|
const { set, cond, add, sub, block, eq, neq, and, divide, greaterThan, greaterOrEq, Value, spring, lessThan, lessOrEq, multiply } =
|
|
Animated;
|
|
|
|
if (!Animated.proc) {
|
|
throw new Error('Incompatible Reanimated version (proc not found)');
|
|
}
|
|
|
|
// clock procs don't seem to work in web, not sure if there's a perf benefit to web procs anyway?
|
|
const proc = isWeb ? <T>(cb: T) => cb : Animated.proc;
|
|
|
|
export const getIsAfterActive = proc((currentIndex: Animated.Node<number>, activeIndex: Animated.Node<number>) =>
|
|
greaterThan(currentIndex, activeIndex),
|
|
);
|
|
|
|
export const hardReset = proc(
|
|
(position: Animated.Value<number>, finished: Animated.Value<number>, time: Animated.Value<number>, toValue: Animated.Value<number>) =>
|
|
block([set(position, 0), set(finished, 0), set(time, 0), set(toValue, 0)]),
|
|
);
|
|
|
|
/**
|
|
* The in react-native-reanimated.d.ts definition of `proc` only has generics
|
|
* for up to 10 arguments. We cast it to accept any params to avoid errors when
|
|
* type-checking.
|
|
*/
|
|
type RetypedProc = (cb: (...params: any) => Animated.Node<number>) => typeof cb;
|
|
|
|
export const setupCell = proc(
|
|
(
|
|
currentIndex: Animated.Value<number>,
|
|
size: Animated.Node<number>,
|
|
offset: Animated.Node<number>,
|
|
isAfterActive: Animated.Value<number>,
|
|
prevToValue: Animated.Value<number>,
|
|
prevSpacerIndex: Animated.Value<number>,
|
|
activeIndex: Animated.Node<number>,
|
|
activeCellSize: Animated.Node<number>,
|
|
hoverOffset: Animated.Node<number>,
|
|
spacerIndex: Animated.Value<number>,
|
|
toValue: Animated.Value<number>,
|
|
position: Animated.Value<number>,
|
|
time: Animated.Value<number>,
|
|
finished: Animated.Value<number>,
|
|
runSpring: Animated.Node<number>,
|
|
onFinished: Animated.Node<number>,
|
|
isDraggingCell: Animated.Node<number>,
|
|
placeholderOffset: Animated.Value<number>,
|
|
prevIsDraggingCell: Animated.Value<number>,
|
|
clock: Animated.Clock,
|
|
disabled: Animated.Node<number>,
|
|
) =>
|
|
block([
|
|
cond(
|
|
greaterThan(activeIndex, -1),
|
|
[
|
|
// Only update spacer if touch is not disabled.
|
|
// Fixes android bugs where state would update with invalid touch values on touch end.
|
|
cond(not(disabled), [
|
|
// Determine whether this cell is after the active cell in the list
|
|
set(isAfterActive, getIsAfterActive(currentIndex, activeIndex)),
|
|
|
|
// Determining spacer index is hard to visualize, see diagram: https://i.imgur.com/jRPf5t3.jpg
|
|
cond(
|
|
isAfterActive,
|
|
[
|
|
cond(
|
|
and(
|
|
greaterOrEq(add(hoverOffset, activeCellSize), offset),
|
|
lessThan(add(hoverOffset, activeCellSize), add(offset, divide(size, 2))),
|
|
),
|
|
set(spacerIndex, sub(currentIndex, 1)),
|
|
),
|
|
cond(
|
|
and(
|
|
greaterOrEq(add(hoverOffset, activeCellSize), add(offset, divide(size, 2))),
|
|
lessThan(add(hoverOffset, activeCellSize), add(offset, size)),
|
|
),
|
|
set(spacerIndex, currentIndex),
|
|
),
|
|
],
|
|
cond(lessThan(currentIndex, activeIndex), [
|
|
cond(
|
|
and(lessThan(hoverOffset, add(offset, size)), greaterOrEq(hoverOffset, add(offset, divide(size, 2)))),
|
|
set(spacerIndex, add(currentIndex, 1)),
|
|
),
|
|
cond(
|
|
and(greaterOrEq(hoverOffset, offset), lessThan(hoverOffset, add(offset, divide(size, 2)))),
|
|
set(spacerIndex, currentIndex),
|
|
),
|
|
]),
|
|
),
|
|
// Set placeholder offset
|
|
cond(eq(spacerIndex, currentIndex), [
|
|
set(placeholderOffset, cond(isAfterActive, add(sub(offset, activeCellSize), size), offset)),
|
|
]),
|
|
]),
|
|
|
|
cond(
|
|
eq(currentIndex, activeIndex),
|
|
[
|
|
// If this cell is the active cell
|
|
cond(
|
|
isDraggingCell,
|
|
[
|
|
// Set its position to the drag position
|
|
set(position, sub(hoverOffset, offset)),
|
|
],
|
|
[
|
|
// Active item, not pressed in
|
|
|
|
// Set value hovering element will snap to once released
|
|
cond(prevIsDraggingCell, [
|
|
set(toValue, sub(placeholderOffset, offset)),
|
|
// The clock starts automatically when toValue changes, however, we need to handle the
|
|
// case where the item should snap back to its original location and toValue doesn't change
|
|
cond(eq(prevToValue, toValue), [
|
|
cond(clockRunning(clock), stopClock(clock)),
|
|
set(time, 0),
|
|
set(finished, 0),
|
|
startClock(clock),
|
|
]),
|
|
]),
|
|
],
|
|
),
|
|
],
|
|
[
|
|
// Not the active item
|
|
// Translate cell down if it is before active index and active cell has passed it.
|
|
// Translate cell up if it is after the active index and active cell has passed it.
|
|
set(
|
|
toValue,
|
|
cond(
|
|
cond(isAfterActive, lessOrEq(currentIndex, spacerIndex), greaterOrEq(currentIndex, spacerIndex)),
|
|
cond(isAfterActive, multiply(activeCellSize, -1), activeCellSize),
|
|
0,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
// If this cell should animate somewhere new, reset its state and start its clock
|
|
cond(neq(toValue, prevToValue), [cond(clockRunning(clock), stopClock(clock)), set(time, 0), set(finished, 0), startClock(clock)]),
|
|
|
|
cond(neq(prevSpacerIndex, spacerIndex), [
|
|
cond(eq(spacerIndex, -1), [
|
|
// Hard reset to prevent stale state bugs
|
|
cond(clockRunning(clock), stopClock(clock)),
|
|
hardReset(position, finished, time, toValue),
|
|
]),
|
|
]),
|
|
cond(finished, [onFinished, set(time, 0), set(finished, 0)]),
|
|
set(prevSpacerIndex, spacerIndex),
|
|
set(prevToValue, toValue),
|
|
set(prevIsDraggingCell, isDraggingCell),
|
|
cond(clockRunning(clock), runSpring),
|
|
],
|
|
[
|
|
// // Reset the spacer index when drag ends
|
|
cond(neq(spacerIndex, -1), set(spacerIndex, -1)),
|
|
cond(neq(position, 0), set(position, 0)),
|
|
],
|
|
),
|
|
position,
|
|
]),
|
|
);
|
|
|
|
const betterSpring = (proc as RetypedProc)(
|
|
(
|
|
finished: Animated.Value<number>,
|
|
velocity: Animated.Value<number>,
|
|
position: Animated.Value<number>,
|
|
time: Animated.Value<number>,
|
|
prevPosition: Animated.Value<number>,
|
|
toValue: Animated.Value<number>,
|
|
damping: Animated.Value<number>,
|
|
mass: Animated.Value<number>,
|
|
stiffness: Animated.Value<number>,
|
|
overshootClamping: Animated.SpringConfig['overshootClamping'],
|
|
restSpeedThreshold: Animated.Value<number>,
|
|
restDisplacementThreshold: Animated.Value<number>,
|
|
clock: Animated.Clock,
|
|
) =>
|
|
spring(
|
|
clock,
|
|
{
|
|
finished,
|
|
velocity,
|
|
position,
|
|
time,
|
|
// @ts-ignore -- https://github.com/software-mansion/react-native-reanimated/blob/master/src/animations/spring.js#L177
|
|
prevPosition,
|
|
},
|
|
{
|
|
toValue,
|
|
damping,
|
|
mass,
|
|
stiffness,
|
|
overshootClamping,
|
|
restDisplacementThreshold,
|
|
restSpeedThreshold,
|
|
},
|
|
),
|
|
);
|
|
|
|
export function springFill(clock: Animated.Clock, state: Animated.SpringState, config: Animated.SpringConfig) {
|
|
return betterSpring(
|
|
state.finished,
|
|
state.velocity,
|
|
state.position,
|
|
state.time,
|
|
new Value(0),
|
|
config.toValue,
|
|
config.damping,
|
|
config.mass,
|
|
config.stiffness,
|
|
config.overshootClamping,
|
|
config.restSpeedThreshold,
|
|
config.restDisplacementThreshold,
|
|
clock,
|
|
);
|
|
}
|