import { useEffect, useCallback, useRef } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useAppSelector } from './useAppSelector'; import { useAppDispatch } from './useAppDispatch'; import { fetchTask, setSelectedTaskId, setShowTaskDrawer, } from '@/features/task-drawer/task-drawer.slice'; /** * A custom hook that synchronizes the task drawer state with the URL. * When the task drawer is opened, it adds the task ID to the URL as a query parameter. * When the task drawer is closed, it removes the task ID from the URL. * It also checks for a task ID in the URL when the component mounts and opens the task drawer if one is found. */ const useTaskDrawerUrlSync = () => { const [searchParams, setSearchParams] = useSearchParams(); const dispatch = useAppDispatch(); const { showTaskDrawer, selectedTaskId } = useAppSelector(state => state.taskDrawerReducer); const { projectId } = useAppSelector(state => state.projectReducer); // Use a ref to track whether we're in the process of closing the drawer const isClosingDrawer = useRef(false); // Use a ref to track the last task ID we processed const lastProcessedTaskId = useRef(null); // Use a ref to track if we should ignore URL changes (when programmatically updating) const shouldIgnoreUrlChange = useRef(false); // Function to clear the task parameter from URL const clearTaskFromUrl = useCallback(() => { if (searchParams.has('task')) { // Set the flag to indicate we're closing the drawer isClosingDrawer.current = true; shouldIgnoreUrlChange.current = true; // Create a new URLSearchParams object to avoid modifying the current one const newParams = new URLSearchParams(searchParams); newParams.delete('task'); // Update the URL without triggering a navigation setSearchParams(newParams, { replace: true }); // Reset the flags after a short delay setTimeout(() => { isClosingDrawer.current = false; shouldIgnoreUrlChange.current = false; }, 300); // Increased timeout to ensure proper cleanup } }, [searchParams, setSearchParams]); // Check for task ID in URL when it changes useEffect(() => { // Skip if we're programmatically updating the URL or closing the drawer if (shouldIgnoreUrlChange.current || isClosingDrawer.current) return; const taskIdFromUrl = searchParams.get('task'); // Only process URL changes if: // 1. There's a task ID in the URL // 2. The drawer is not currently open // 3. We have a project ID // 4. It's a different task ID than what we last processed // 5. The selected task ID is different from URL (to avoid reopening same task) if ( taskIdFromUrl && !showTaskDrawer && projectId && taskIdFromUrl !== lastProcessedTaskId.current && taskIdFromUrl !== selectedTaskId ) { lastProcessedTaskId.current = taskIdFromUrl; dispatch(setSelectedTaskId(taskIdFromUrl)); dispatch(setShowTaskDrawer(true)); // Fetch task data dispatch(fetchTask({ taskId: taskIdFromUrl, projectId })); } }, [searchParams, showTaskDrawer, projectId, selectedTaskId, dispatch]); // Update URL when task drawer state changes useEffect(() => { // Don't update URL if we're in the process of closing or ignoring changes if (isClosingDrawer.current || shouldIgnoreUrlChange.current) return; if (showTaskDrawer && selectedTaskId) { // Don't update if it's the same task ID we already processed if (lastProcessedTaskId.current === selectedTaskId) return; // Add task ID to URL when drawer is opened shouldIgnoreUrlChange.current = true; lastProcessedTaskId.current = selectedTaskId; // Create a new URLSearchParams object to avoid modifying the current one const newParams = new URLSearchParams(searchParams); newParams.set('task', selectedTaskId); // Update the URL without triggering a navigation setSearchParams(newParams, { replace: true }); // Reset the flag after a short delay setTimeout(() => { shouldIgnoreUrlChange.current = false; }, 100); } }, [showTaskDrawer, selectedTaskId, searchParams, setSearchParams]); // Separate effect to handle URL clearing when drawer is closed useEffect(() => { // Only clear URL when drawer is closed and we have a task in URL // Also ensure we're not in the middle of processing other URL changes if ( !showTaskDrawer && searchParams.has('task') && !isClosingDrawer.current && !shouldIgnoreUrlChange.current && !selectedTaskId ) { // Only clear if selectedTaskId is also null/cleared clearTaskFromUrl(); lastProcessedTaskId.current = null; } }, [showTaskDrawer, searchParams, selectedTaskId, clearTaskFromUrl]); return { clearTaskFromUrl }; }; export default useTaskDrawerUrlSync;