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
43 changes: 19 additions & 24 deletions mobile/modules/toast/src/components/Toast.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useEffect } from "react";
import { StyleSheet, Text, useWindowDimensions } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { GestureDetector, usePanGesture } from "react-native-gesture-handler";
import Animated, {
useAnimatedStyle,
useSharedValue,
Expand Down Expand Up @@ -36,28 +36,23 @@ export function Toast({ toast, exiting, theme }: Props) {
//#region Dismiss Gesture
const panAmount = useSharedValue(0);

const panGesture = useMemo(
() =>
Gesture.Pan()
.enabled(!exiting)
.activeOffsetY([-10, 10])
.onUpdate(({ translationY }) =>
panAmount.set(Math.min(0, translationY)),
)
.onEnd(({ velocityY }) => {
const metThreshold = velocityY < -500;
panAmount.set(
withSpring(
metThreshold ? -(topOffset + toastHeight.get() + 256) : 0,
undefined,
(finished) => {
if (finished && metThreshold) scheduleOnRN(onRemove);
},
),
);
}),
[panAmount, toastHeight, topOffset, exiting, onRemove],
);
const panGesture = usePanGesture({
enabled: !exiting,
activeOffsetY: [-10, 10],
onUpdate: ({ translationY }) => panAmount.set(Math.min(0, translationY)),
onDeactivate: ({ velocityY }) => {
const metThreshold = velocityY < -500;
panAmount.set(
withSpring(
metThreshold ? -(topOffset + toastHeight.get() + 256) : 0,
undefined,
(finished) => {
if (finished && metThreshold) scheduleOnRN(onRemove);
},
),
);
},
});
//#endregion

//#region Enter/Exit Animations
Expand Down
2 changes: 1 addition & 1 deletion mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"react-native-android-widget": "0.20.3",
"react-native-audio-browser": "github:MissingCore/react-native-audio-browser#bb9da301fdc284e144a54488c83c1af87b0ba61f",
"react-native-bootsplash": "7.3.1",
"react-native-gesture-handler": "2.31.2",
"react-native-gesture-handler": "3.0.0",
"react-native-keyboard-controller": "1.21.8",
"react-native-markdown-renderer": "4.1.1",
"react-native-nitro-modules": "0.35.7",
Expand Down
32 changes: 5 additions & 27 deletions mobile/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

121 changes: 56 additions & 65 deletions mobile/src/components/DragList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { createContext, use, useCallback, useMemo, useRef } from "react";
import {
createContext,
use,
useCallback,
useMemo,
useRef,
useState,
} from "react";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
GestureDetector,
useNativeGesture,
usePanGesture,
useSimultaneousGestures,
} from "react-native-gesture-handler";
import type { SharedValue } from "react-native-reanimated";
import Animated, {
clamp,
Expand All @@ -23,6 +21,7 @@ import { scheduleOnRN, scheduleOnUI } from "react-native-worklets";
import type { StoreApi } from "zustand";
import { createStore, useStore } from "zustand";

import { cn } from "~/lib/style";
import type { LegendListProps, ListRenderItemInfo } from "./Base/LegendList";
import { LegendList, useAnimatedLegendListRef } from "./Base/LegendList";

Expand All @@ -47,6 +46,9 @@ interface DragListProps<TData> extends Omit<
onDragEnd?: VoidFunction;
/** Called when an item is successfully moved. */
onReordered: (fromIndex: number, toIndex: number) => void;

/** If default stylings to fix "fake gaps" will be applied. Defaults to `true`. */
useDefaultStyles?: boolean;
}

const INACTIVE = -1;
Expand All @@ -66,12 +68,13 @@ function DragListImpl<TData>({
data,
renderItem,
estimatedItemSize,
useDefaultStyles = true,
onDragBegin: _,
onDragEnd,
onReordered,
...props
}: DragListProps<TData>) {
const [enabled, setEnabled] = useState(true);
const enabled = useSharedValue(true);

const dataRef = useRef(data);
const pan = useDragListStore((s) => s.pan);
Expand Down Expand Up @@ -122,9 +125,10 @@ function DragListImpl<TData>({
autoScrollAmount.set(0);
pan.set(0);
shifted.set(0);
enabled.set(true);
});
setEnabled(true);
}, [
enabled,
setReactiveActiveIndex,
autoScrollDirection,
autoScrollAmount,
Expand All @@ -133,55 +137,39 @@ function DragListImpl<TData>({
shifted,
]);

const panListenerGesture = useMemo(
() =>
Gesture.Pan()
.enabled(enabled)
.onStart(() => {
// Bail out of gesture early.
if (activeIndex.get() === INACTIVE) scheduleOnRN(setEnabled, false);
})
.onUpdate(({ translationY, y }) => {
//? Stop auto-scroll when we move (clears the timer which continues the auto-scroll).
autoScrollDirection.set(0);
if (activeIndex.get() === INACTIVE) return;
pan.set(autoScrollAmount.get() + translationY);
revalidatedShifted();

//? Auto-scroll handling.
let direction = 0;
if (y < estimatedItemSize) direction = -1;
else if (y > listHeight.get() - estimatedItemSize) direction = 1;
autoScrollDirection.set(direction);
})
.onFinalize(() => {
// Ensure we reset the gesture focus.
scheduleOnRN(setEnabled, false);
if (activeIndex.get() !== INACTIVE) {
if (onDragEnd) scheduleOnRN(onDragEnd);
scheduleOnRN(
onReordered,
activeIndex.get(),
activeIndex.get() + shifted.get(),
);
}
scheduleOnRN(onCleanup);
}),
[
enabled,
listHeight,
autoScrollDirection,
autoScrollAmount,
revalidatedShifted,
estimatedItemSize,
activeIndex,
pan,
onDragEnd,
onReordered,
onCleanup,
shifted,
],
);
const panListenerGesture = usePanGesture({
enabled,
onActivate: () => {
// Bail out of gesture early.
if (activeIndex.get() === INACTIVE) enabled.set(false);
},
onUpdate: ({ translationY, y }) => {
//? Stop auto-scroll when we move (clears the timer which continues the auto-scroll).
autoScrollDirection.set(0);
if (activeIndex.get() === INACTIVE) return;
pan.set(autoScrollAmount.get() + translationY);
revalidatedShifted();

//? Auto-scroll handling.
let direction = 0;
if (y < estimatedItemSize) direction = -1;
else if (y > listHeight.get() - estimatedItemSize) direction = 1;
autoScrollDirection.set(direction);
},
onFinalize: () => {
// Ensure we reset the gesture focus.
enabled.set(false);
if (activeIndex.get() !== INACTIVE) {
if (onDragEnd) scheduleOnRN(onDragEnd);
scheduleOnRN(
onReordered,
activeIndex.get(),
activeIndex.get() + shifted.get(),
);
}
scheduleOnRN(onCleanup);
},
});

useAnimatedReaction(
() => autoScrollDirection.get(),
Expand All @@ -206,11 +194,9 @@ function DragListImpl<TData>({
},
);

const gesture = useMemo(
// `Gesture.Native()` allows for scroll to work.
() => Gesture.Simultaneous(Gesture.Native(), panListenerGesture),
[panListenerGesture],
);
// The "Native" gesture allows for scroll to work.
const nativeGesture = useNativeGesture({});
const gestures = useSimultaneousGestures(nativeGesture, panListenerGesture);

const renderDragItem = useCallback(
(info: ListRenderItemInfo<TData>) => (
Expand All @@ -228,7 +214,7 @@ function DragListImpl<TData>({
}

return (
<GestureDetector gesture={gesture}>
<GestureDetector gesture={gestures}>
<LegendList
{...props}
ref={listRef}
Expand All @@ -245,6 +231,11 @@ function DragListImpl<TData>({
scrollEnabled={reactiveActiveIndex === INACTIVE}
// Fixes some issues caused by recycling.
extraData={reactiveActiveIndex}
className={cn({ "-mb-2": useDefaultStyles }, props.className)}
contentContainerClassName={cn(
{ "py-4": useDefaultStyles },
props.contentContainerClassName,
)}
/>
</GestureDetector>
);
Expand Down
2 changes: 0 additions & 2 deletions mobile/src/components/Form/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export function NumericInput({
);

return (
// @ts-expect-error - Ref is compatible.
<Input
inputMode="numeric"
//? The order of where we define certain props is important with
Expand Down Expand Up @@ -79,7 +78,6 @@ export function TextInput({
);

return (
// @ts-expect-error - Ref is compatible.
<Input
textAlign={OnRTL.decide("right", "left")}
placeholderTextColorClassName="accent-onSurfaceVariant"
Expand Down
Loading