fix: disable Telegram vertical swipes during drag operations

- Add useTelegramDnd hook for @dnd-kit integration
- Update Sheet components to disable swipes when open
- Update AdminPaymentMethods to use the new hook
- Prevents conflict between app drag gestures and Telegram swipe-to-close
This commit is contained in:
c0mrade
2026-02-01 22:46:16 +03:00
parent 0773afdf6e
commit 8deca2fa5b
4 changed files with 132 additions and 13 deletions

View File

@@ -7,10 +7,12 @@ import {
useContext,
useState,
useCallback,
useEffect,
} from 'react';
import { cn } from '@/lib/utils';
import { usePlatform } from '@/platform';
import { useBackButton } from '@/platform';
import { useTelegramSDK } from '@/hooks/useTelegramSDK';
import {
backdrop,
backdropTransition,
@@ -138,10 +140,26 @@ export const SheetContent = forwardRef<HTMLDivElement, SheetContentProps>(
const { open, onClose } = useContext(SheetContext);
const { haptic } = usePlatform();
const dragControls = useDragControls();
const { disableVerticalSwipes, enableVerticalSwipes, isTelegramWebApp } = useTelegramSDK();
// Back button integration
useBackButton(open ? onClose : null);
// Disable Telegram vertical swipes when sheet is open (prevents conflict with drag gestures)
useEffect(() => {
if (!isTelegramWebApp) return;
if (open) {
disableVerticalSwipes();
}
return () => {
if (open) {
enableVerticalSwipes();
}
};
}, [open, isTelegramWebApp, disableVerticalSwipes, enableVerticalSwipes]);
const handleDragEnd = useCallback(
(_: MouseEvent | TouchEvent | PointerEvent, info: PanInfo) => {
const velocity = info.velocity.y;

View File

@@ -2,6 +2,7 @@ import { useEffect, useRef, useCallback, useState, type ReactNode } from 'react'
import { createPortal } from 'react-dom';
import { useBackButton } from '@/platform';
import { useHaptic } from '@/platform';
import { useTelegramSDK } from '@/hooks/useTelegramSDK';
export interface SheetProps {
isOpen: boolean;
@@ -104,10 +105,26 @@ export function Sheet({
const [isVisible, setIsVisible] = useState(false);
const haptic = useHaptic();
const { disableVerticalSwipes, enableVerticalSwipes, isTelegramWebApp } = useTelegramSDK();
// BackButton integration
useBackButton(isOpen ? onClose : null);
// Disable Telegram vertical swipes when sheet is open (prevents conflict with drag gestures)
useEffect(() => {
if (!isTelegramWebApp) return;
if (isOpen) {
disableVerticalSwipes();
}
return () => {
if (isOpen) {
enableVerticalSwipes();
}
};
}, [isOpen, isTelegramWebApp, disableVerticalSwipes, enableVerticalSwipes]);
// Calculate current height based on snap point
const currentHeight = `${snapPoints[currentSnapIndex] * 100}vh`;

View File

@@ -0,0 +1,55 @@
import { useCallback, useRef } from 'react';
import { useTelegramSDK } from './useTelegramSDK';
/**
* Hook to manage Telegram vertical swipes during drag-and-drop operations.
* Disables Telegram's swipe-to-close gesture while dragging to prevent conflicts.
*
* Usage with @dnd-kit:
* ```tsx
* const { onDragStart, onDragEnd } = useTelegramDnd();
*
* <DndContext
* onDragStart={(e) => {
* onDragStart();
* // your drag start logic
* }}
* onDragEnd={(e) => {
* onDragEnd();
* // your drag end logic
* }}
* >
* ```
*/
export function useTelegramDnd() {
const { disableVerticalSwipes, enableVerticalSwipes, isTelegramWebApp } = useTelegramSDK();
const isDraggingRef = useRef(false);
const onDragStart = useCallback(() => {
if (isTelegramWebApp && !isDraggingRef.current) {
isDraggingRef.current = true;
disableVerticalSwipes();
}
}, [isTelegramWebApp, disableVerticalSwipes]);
const onDragEnd = useCallback(() => {
if (isTelegramWebApp && isDraggingRef.current) {
isDraggingRef.current = false;
enableVerticalSwipes();
}
}, [isTelegramWebApp, enableVerticalSwipes]);
const onDragCancel = useCallback(() => {
if (isTelegramWebApp && isDraggingRef.current) {
isDraggingRef.current = false;
enableVerticalSwipes();
}
}, [isTelegramWebApp, enableVerticalSwipes]);
return {
onDragStart,
onDragEnd,
onDragCancel,
isTelegramWebApp,
};
}

View File

@@ -8,7 +8,9 @@ import {
useSensor,
useSensors,
type DragEndEvent,
type DragStartEvent,
} from '@dnd-kit/core';
import { useTelegramDnd } from '../hooks/useTelegramDnd';
import {
arrayMove,
SortableContext,
@@ -674,18 +676,40 @@ export default function AdminPaymentMethods() {
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
);
const handleDragEnd = useCallback((event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over.id) {
setMethods((prev) => {
const oldIndex = prev.findIndex((m) => m.method_id === active.id);
const newIndex = prev.findIndex((m) => m.method_id === over.id);
if (oldIndex === -1 || newIndex === -1) return prev;
return arrayMove(prev, oldIndex, newIndex);
});
setOrderChanged(true);
}
}, []);
// Telegram swipe behavior for drag-and-drop
const {
onDragStart: onTelegramDragStart,
onDragEnd: onTelegramDragEnd,
onDragCancel: onTelegramDragCancel,
} = useTelegramDnd();
const handleDragStart = useCallback(
(_event: DragStartEvent) => {
onTelegramDragStart();
},
[onTelegramDragStart],
);
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
onTelegramDragEnd();
const { active, over } = event;
if (over && active.id !== over.id) {
setMethods((prev) => {
const oldIndex = prev.findIndex((m) => m.method_id === active.id);
const newIndex = prev.findIndex((m) => m.method_id === over.id);
if (oldIndex === -1 || newIndex === -1) return prev;
return arrayMove(prev, oldIndex, newIndex);
});
setOrderChanged(true);
}
},
[onTelegramDragEnd],
);
const handleDragCancel = useCallback(() => {
onTelegramDragCancel();
}, [onTelegramDragCancel]);
const handleSaveOrder = () => {
saveOrderMutation.mutate(methods.map((m) => m.method_id));
@@ -737,7 +761,12 @@ export default function AdminPaymentMethods() {
<div className="h-8 w-8 animate-spin rounded-full border-2 border-accent-500 border-t-transparent" />
</div>
) : methods.length > 0 ? (
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
<DndContext
sensors={sensors}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<SortableContext
items={methods.map((m) => m.method_id)}
strategy={verticalListSortingStrategy}