import { useState, useCallback, useRef, useEffect } from 'react'; export const useCellLocks = (userId, lockTimeout = 30000) => { const [lockedCells, setLockedCells] = useState(new Map()); const lockTimersRef = useRef(new Map()); const getCellKey = (rowId, columnId) => `${rowId}_${columnId}`; const lockCell = useCallback((rowId, columnId, lockingUserId) => { const cellKey = getCellKey(rowId, columnId); setLockedCells(prev => { const newLocks = new Map(prev); const existingLock = newLocks.get(cellKey); if (existingLock && existingLock.userId !== lockingUserId) { return prev; // Cell already locked by another user } newLocks.set(cellKey, { userId: lockingUserId, lockedAt: Date.now(), rowId, columnId }); return newLocks; }); return true; }, []); const unlockCell = useCallback((rowId, columnId, unlockingUserId) => { const cellKey = getCellKey(rowId, columnId); setLockedCells(prev => { const newLocks = new Map(prev); const lock = newLocks.get(cellKey); if (lock && lock.userId === unlockingUserId) { newLocks.delete(cellKey); } return newLocks; }); // Clear timer if exists if (lockTimersRef.current.has(cellKey)) { clearTimeout(lockTimersRef.current.get(cellKey)); lockTimersRef.current.delete(cellKey); } }, []); const tryLockCell = useCallback((rowId, columnId, currentUserId) => { const cellKey = getCellKey(rowId, columnId); const existingLock = lockedCells.get(cellKey); if (existingLock && existingLock.userId !== currentUserId) { return false; // Cell is locked by another user } if (!existingLock) { lockCell(rowId, columnId, currentUserId); // Auto-unlock after timeout const timer = setTimeout(() => { unlockCell(rowId, columnId, currentUserId); }, lockTimeout); lockTimersRef.current.set(cellKey, timer); } return true; }, [lockedCells, lockCell, unlockCell, lockTimeout]); const releaseAllLocks = useCallback((userId) => { setLockedCells(prev => { const newLocks = new Map(prev); for (const [key, lock] of newLocks.entries()) { if (lock.userId === userId) { newLocks.delete(key); if (lockTimersRef.current.has(key)) { clearTimeout(lockTimersRef.current.get(key)); lockTimersRef.current.delete(key); } } } return newLocks; }); }, []); const isCellLocked = useCallback((rowId, columnId, currentUserId) => { const cellKey = getCellKey(rowId, columnId); const lock = lockedCells.get(cellKey); return lock && lock.userId !== currentUserId; }, [lockedCells]); const getCellLockInfo = useCallback((rowId, columnId) => { const cellKey = getCellKey(rowId, columnId); return lockedCells.get(cellKey); }, [lockedCells]); useEffect(() => { return () => { // Clear all timers on unmount for (const timer of lockTimersRef.current.values()) { clearTimeout(timer); } lockTimersRef.current.clear(); }; }, []); return { lockedCells, lockCell, unlockCell, tryLockCell, isCellLocked, getCellLockInfo, releaseAllLocks }; };