feat: refactor long-press functionality in AllPrivateChatsSidebar and AllSharedChatsSidebar to utilize custom hook for improved code reusability and maintainability

This commit is contained in:
Anish Sarkar 2026-03-07 04:44:24 +05:30
parent 2ea67c1764
commit 37e1995546
3 changed files with 41 additions and 49 deletions

View file

@ -17,6 +17,7 @@ import {
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useLongPress } from "@/hooks/use-long-press";
import { toast } from "sonner"; import { toast } from "sonner";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -85,23 +86,14 @@ export function AllPrivateChatsSidebar({
const [isRenaming, setIsRenaming] = useState(false); const [isRenaming, setIsRenaming] = useState(false);
const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);
const longPressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); const pendingThreadIdRef = useRef<number | null>(null);
const longPressTriggeredRef = useRef(false); const { handlers: longPressHandlers, wasLongPress } = useLongPress(
useCallback(() => {
const handleLongPressStart = useCallback((threadId: number) => { if (pendingThreadIdRef.current !== null) {
longPressTriggeredRef.current = false; setOpenDropdownId(pendingThreadIdRef.current);
longPressTimerRef.current = setTimeout(() => { }
longPressTriggeredRef.current = true; }, [])
setOpenDropdownId(threadId); );
}, 500);
}, []);
const handleLongPressCancel = useCallback(() => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
longPressTimerRef.current = null;
}
}, []);
const isSearchMode = !!debouncedSearchQuery.trim(); const isSearchMode = !!debouncedSearchQuery.trim();
@ -376,15 +368,15 @@ export function AllPrivateChatsSidebar({
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
if (longPressTriggeredRef.current) { if (wasLongPress()) return;
longPressTriggeredRef.current = false;
return;
}
handleThreadClick(thread.id); handleThreadClick(thread.id);
}} }}
onTouchStart={() => handleLongPressStart(thread.id)} onTouchStart={() => {
onTouchEnd={handleLongPressCancel} pendingThreadIdRef.current = thread.id;
onTouchMove={handleLongPressCancel} longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy} disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden" className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
> >

View file

@ -17,6 +17,7 @@ import {
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useLongPress } from "@/hooks/use-long-press";
import { toast } from "sonner"; import { toast } from "sonner";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -85,23 +86,14 @@ export function AllSharedChatsSidebar({
const [isRenaming, setIsRenaming] = useState(false); const [isRenaming, setIsRenaming] = useState(false);
const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300);
const longPressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); const pendingThreadIdRef = useRef<number | null>(null);
const longPressTriggeredRef = useRef(false); const { handlers: longPressHandlers, wasLongPress } = useLongPress(
useCallback(() => {
const handleLongPressStart = useCallback((threadId: number) => { if (pendingThreadIdRef.current !== null) {
longPressTriggeredRef.current = false; setOpenDropdownId(pendingThreadIdRef.current);
longPressTimerRef.current = setTimeout(() => { }
longPressTriggeredRef.current = true; }, [])
setOpenDropdownId(threadId); );
}, 500);
}, []);
const handleLongPressCancel = useCallback(() => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
longPressTimerRef.current = null;
}
}, []);
const isSearchMode = !!debouncedSearchQuery.trim(); const isSearchMode = !!debouncedSearchQuery.trim();
@ -376,15 +368,15 @@ export function AllSharedChatsSidebar({
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
if (longPressTriggeredRef.current) { if (wasLongPress()) return;
longPressTriggeredRef.current = false;
return;
}
handleThreadClick(thread.id); handleThreadClick(thread.id);
}} }}
onTouchStart={() => handleLongPressStart(thread.id)} onTouchStart={() => {
onTouchEnd={handleLongPressCancel} pendingThreadIdRef.current = thread.id;
onTouchMove={handleLongPressCancel} longPressHandlers.onTouchStart();
}}
onTouchEnd={longPressHandlers.onTouchEnd}
onTouchMove={longPressHandlers.onTouchMove}
disabled={isBusy} disabled={isBusy}
className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden" className="flex items-center gap-2 flex-1 min-w-0 text-left overflow-hidden"
> >

View file

@ -1,4 +1,4 @@
import { useCallback, useRef } from "react"; import { useCallback, useEffect, useRef } from "react";
const LONG_PRESS_DELAY = 500; const LONG_PRESS_DELAY = 500;
@ -21,6 +21,14 @@ export function useLongPress(onLongPress: () => void, delay = LONG_PRESS_DELAY)
} }
}, []); }, []);
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, []);
const handlers = { const handlers = {
onTouchStart: start, onTouchStart: start,
onTouchEnd: cancel, onTouchEnd: cancel,