Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function NativeDetector<THandlerData, TConfig>({
onGestureHandlerReanimatedTouchEvent={
gesture.detectorCallbacks.onReanimatedTouchEvent
}
// @ts-ignore This is a type mismatch between RNGH types and RN Codegen types
onGestureHandlerAnimatedEvent={
gesture.detectorCallbacks.onGestureHandlerAnimatedEvent
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,15 @@ export function InterceptingGestureDetector<THandlerData, TConfig>({
const createGestureEventHandler = useCallback(
(key: keyof DetectorCallbacks<THandlerData>) => {
return (e: GestureHandlerEvent<THandlerData>) => {
if (gesture?.detectorCallbacks[key]) {
if (typeof gesture?.detectorCallbacks[key] === 'function') {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also covering undefined check, typeof undefined === 'undefined'

// @ts-expect-error passing event to a union of functions where only one is typed as such
gesture.detectorCallbacks[key](e);
}

virtualChildren.forEach((child) => {
const method = child.methods[key];
if (method) {
if (typeof method === 'function') {
// @ts-expect-error passing event to a union of functions where only one is typed as such
Comment on lines +129 to +137

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those types.

method(e);
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { prepareUpdateHandlers, isAnimatedEvent } from '../../utils';
import { prepareUpdateHandlers } from '../../utils';
import { ReanimatedContext } from '../../../../handlers/gestures/reanimatedWrapper';
import { getUpdateHandler } from '../updateHandler';
import { BaseGestureConfig } from '../../../types';
Expand All @@ -20,13 +20,18 @@ export function useGestureUpdateEvent<THandlerData, TConfig>(
lastUpdateEvent: undefined,
};

return isAnimatedEvent(config.onUpdate)
return config.dispatchesAnimatedEvents
? undefined
: getUpdateHandler(
handlerTag,
handlers,
jsContext,
changeEventCalculator
);
}, [handlerTag, config.onUpdate, config.changeEventCalculator]);
}, [
handlerTag,
config.onUpdate,
config.dispatchesAnimatedEvents,
config.changeEventCalculator,
]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,6 @@ export function useGesture<THandlerData, TConfig>(
// This has to be done ASAP as other hooks depend `shouldUseReanimatedDetector`.
prepareConfig(config);

if (config.dispatchesAnimatedEvents && config.shouldUseReanimatedDetector) {
throw new Error(
tagMessage(
`${type}: You cannot use Animated.Event together with callbacks running on the UI thread. Either remove Animated.Event from onUpdate, or set runOnJS property to true on the gesture.`
)
);
}

// TODO: Call only necessary hooks depending on which callbacks are defined (?)
const {
onGestureHandlerStateChange,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
import { useGestureStateChangeEvent } from './callbacks/js/useGestureStateChangeEvent';
import { useGestureUpdateEvent } from './callbacks/js/useGestureUpdateEvent';
import { useGestureTouchEvent } from './callbacks/js/useGestureTouchEvent';
import { AnimatedEvent, BaseGestureConfig } from '../types';
import { checkMappingForChangeProperties, isAnimatedEvent } from './utils';
import { AnimatedEvent, BaseGestureConfig, GestureUpdateEvent } from '../types';
import {
checkMappingForChangeProperties,
isNativeAnimatedEvent,
} from './utils';
import { useReanimatedStateChangeEvent } from './callbacks/reanimated/useReanimatedStateChangeEvent';
import { useReanimatedUpdateEvent } from './callbacks/reanimated/useReanimatedUpdateEvent';
import { useReanimatedTouchEvent } from './callbacks/reanimated/useReanimatedTouchEvent';
import { tagMessage } from '../../utils';

function guardJSAnimatedEvent(handler: (...args: unknown[]) => void) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fun of this name, but I cannot think of any better.

return (...args: unknown[]) => {
try {
handler(...args);
} catch (e) {
if (
e instanceof Error &&
e.message.includes('Bad event of type undefined for key')
) {
throw new Error(
tagMessage(
'The event mapping inside an Animated.event is invalid. ' +
'Please make sure you are using the correct structure for the gesture event:\n\n' +
'{ nativeEvent: { handlerData: { /* your mappings here */ } } }'
)
);
}

throw e;
}
};
}

export function useGestureCallbacks<THandlerData, TConfig>(
handlerTag: number,
Expand All @@ -31,10 +58,21 @@ export function useGestureCallbacks<THandlerData, TConfig>(
onReanimatedTouchEvent = useReanimatedTouchEvent(handlerTag, config);
}

let onGestureHandlerAnimatedEvent: AnimatedEvent | undefined;
if (isAnimatedEvent(config.onUpdate)) {
checkMappingForChangeProperties(config.onUpdate);
onGestureHandlerAnimatedEvent = config.onUpdate;
let onGestureHandlerAnimatedEvent:
| ((event: GestureUpdateEvent<THandlerData>) => void)
| AnimatedEvent
| undefined;
if (config.dispatchesAnimatedEvents) {
if (__DEV__ && isNativeAnimatedEvent(config.onUpdate)) {
checkMappingForChangeProperties(config.onUpdate);
}

if (__DEV__ && !isNativeAnimatedEvent(config.onUpdate)) {
// @ts-expect-error At this point we know it's not a native animated event, so it's callable
onGestureHandlerAnimatedEvent = guardJSAnimatedEvent(config.onUpdate);
} else {
onGestureHandlerAnimatedEvent = config.onUpdate;
}
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SingleGestureName,
} from '../../types';
import { hasWorkletEventHandlers, maybeUnpackValue } from './reanimatedUtils';
import { isAnimatedEvent, shouldHandleTouchEvents } from './eventUtils';
import { isNativeAnimatedEvent, shouldHandleTouchEvents } from './eventUtils';
import {
allowedNativeProps,
EMPTY_WHITE_LIST,
Expand All @@ -20,12 +20,46 @@ export function prepareConfig<THandlerData, TConfig extends object>(
) {
const runOnJS = maybeUnpackValue(config.runOnJS);

if (
__DEV__ &&
isNativeAnimatedEvent(config.onUpdate) &&
!config.useAnimated
) {
console.warn(
tagMessage(
'You are using Animated.event in onUpdate without setting useAnimated to true. ' +
'This may lead to unexpected behavior. If you intend to use Animated.event, ' +
'please set useAnimated to true in the gesture config.'
)
);
}

config.dispatchesAnimatedEvents =
config.useAnimated || isNativeAnimatedEvent(config.onUpdate);

// Validate that the user is not trying to mix Animated and Reanimated before updating the config.
if (
__DEV__ &&
config.dispatchesAnimatedEvents &&
(config.disableReanimated === false || config.runOnJS === false)
) {
throw new Error(
tagMessage(
'Animated cannot be used together with Reanimated in the same gesture. Please choose either Animated or Reanimated for handling gesture events.'
)
);
}

if (config.dispatchesAnimatedEvents) {
config.disableReanimated = true;
}

config.shouldUseReanimatedDetector =
!config.disableReanimated &&
Reanimated !== undefined &&
hasWorkletEventHandlers(config);
hasWorkletEventHandlers(config) &&
!config.dispatchesAnimatedEvents;
config.needsPointerData = shouldHandleTouchEvents(config);
config.dispatchesAnimatedEvents = isAnimatedEvent(config.onUpdate);
config.dispatchesReanimatedEvents =
config.shouldUseReanimatedDetector && !runOnJS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function isEventForHandlerWithTag<THandlerData>(
return event.handlerTag === handlerTag;
}

export function isAnimatedEvent<THandlerData>(
export function isNativeAnimatedEvent<THandlerData>(
callback:
| ((event: GestureUpdateEvent<THandlerData>) => void)
| AnimatedEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export const PropsToFilter = new Set<BaseGestureConfig<unknown, unknown>>([
'changeEventCalculator',
'disableReanimated',
'shouldUseReanimatedDetector',
'useAnimated',
'runOnJS',

// Relations
'simultaneousWithExternalGesture',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export type GestureCallbacks<THandlerData> = {
event: GestureStateChangeEvent<THandlerData>,
didSucceed: boolean
) => void;
onUpdate?: (event: GestureUpdateEvent<THandlerData>) => void | AnimatedEvent;
onUpdate?:
| ((event: GestureUpdateEvent<THandlerData>) => void)
| AnimatedEvent;
Comment on lines +28 to +30

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a function or an AnimatedEvent, it was a function that was returning void or AnimatedEvent.

This was irritating, as it was the reason types in InterceptingGestureDetector were correct.

onTouchesDown?: (event: GestureTouchEvent) => void;
onTouchesMove?: (event: GestureTouchEvent) => void;
onTouchesUp?: (event: GestureTouchEvent) => void;
Expand All @@ -48,6 +50,7 @@ export type InternalConfigProps<THandlerData> = {

export type CommonGestureConfig = {
disableReanimated?: boolean;
useAnimated?: boolean;
} & WithSharedValue<
{
runOnJS?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
StateChangeEvent,
UpdateEvent,
TouchEvent,
GestureUpdateEvent,
} from './EventTypes';

export type DetectorCallbacks<THandlerData> = {
Expand All @@ -18,7 +19,10 @@ export type DetectorCallbacks<THandlerData> = {
| undefined
| ((event: UpdateEvent<THandlerData>) => void);
onReanimatedTouchEvent: undefined | ((event: TouchEvent) => void);
onGestureHandlerAnimatedEvent: undefined | AnimatedEvent;
onGestureHandlerAnimatedEvent:
| undefined
| AnimatedEvent
| ((event: GestureUpdateEvent<THandlerData>) => void);
};

export type VirtualChild = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ export type TouchEvent =
| GestureTouchEvent
| NativeSyntheticEvent<GestureTouchEvent>;

// This is almost how Animated.event is typed in React Native. We add _argMapping in order to:
// 1. Distinguish it from a regular function,
// 2. Have access to the _argMapping property to check for usage of `change*` callbacks.
export type AnimatedEvent = ((...args: any[]) => void) & {
// This is not how Animated.event is typed in React Native. We add _argMapping in order to
// have access to the _argMapping property to check for usage of `change*` callbacks.
// It's also not typed as a function, which is breaking Gesture Handler type definitions.
export type AnimatedEvent = {
_argMapping: (Animated.Mapping | null)[];
};
Comment on lines +54 to 56

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After fixing the type above, this started breaking type inference and every onUpdate was being typed as (...any) => void


Expand Down
Loading