PotapovaA 93881c7194 ws
2026-05-26 19:06:01 +03:00

122 lines
3.3 KiB
JavaScript

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
};
};