Dev #4

Merged
sandrodach merged 12 commits from dev into main 2026-10-25 21:09:45 +01:00
Showing only changes of commit 634fdcac11 - Show all commits
@@ -246,9 +246,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
} }
}; };
const ensureLock = async (): Promise<boolean> => { const ensureLock = async (force = false): Promise<boolean> => {
if (!selectedDoc || selectedDoc.permission === 'read') return false; if (!selectedDoc || selectedDoc.permission === 'read') return false;
if (holdsLockRef.current) { if (holdsLockRef.current && !force) {
return true; return true;
} }
try { try {
@@ -287,10 +287,14 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
if (idleUnlockTimerRef.current) { if (idleUnlockTimerRef.current) {
clearTimeout(idleUnlockTimerRef.current); clearTimeout(idleUnlockTimerRef.current);
} }
// If a block is currently being dragged, do not schedule an unlock
if (isDraggingBlockRef.current) {
return;
}
// Short grace period: if user clicked into another block in the doc, focus will cancel this. // Short grace period: if user clicked into another block in the doc, focus will cancel this.
// If user clicked outside or stopped editing, save and release the lock immediately! // If user clicked outside or stopped editing, save and release the lock immediately!
idleUnlockTimerRef.current = setTimeout(async () => { idleUnlockTimerRef.current = setTimeout(async () => {
if (!isEditorFocusedRef.current && holdsLockRef.current) { if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) {
await flushPendingSave(); await flushPendingSave();
await releaseMyLock(); await releaseMyLock();
} }
@@ -523,13 +527,13 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
// Window blur / visibility change listener: release lock when user leaves tab/window // Window blur / visibility change listener: release lock when user leaves tab/window
useEffect(() => { useEffect(() => {
const handleWindowBlur = () => { const handleWindowBlur = () => {
if (!isEditorFocusedRef.current && holdsLockRef.current) { if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) {
flushPendingSave(); flushPendingSave();
releaseMyLock(); releaseMyLock();
} }
}; };
const handleVisibilityChange = () => { const handleVisibilityChange = () => {
if (document.visibilityState === 'hidden') { if (document.visibilityState === 'hidden' && !isDraggingBlockRef.current) {
isEditorFocusedRef.current = false; isEditorFocusedRef.current = false;
flushPendingSave(); flushPendingSave();
releaseMyLock(); releaseMyLock();
@@ -1957,8 +1961,9 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
clearTimeout(idleUnlockTimerRef.current); clearTimeout(idleUnlockTimerRef.current);
idleUnlockTimerRef.current = null; idleUnlockTimerRef.current = null;
} }
// Activate write lock immediately upon dragging // Activate write lock immediately upon dragging (optimistic UI + backend broadcast)
ensureLock(); setHoldsLock(true);
ensureLock(true);
e.dataTransfer.setData('application/mindsift-block-id', blockId); e.dataTransfer.setData('application/mindsift-block-id', blockId);
e.dataTransfer.setData('text/plain', blockId); e.dataTransfer.setData('text/plain', blockId);
@@ -1966,6 +1971,21 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
setDraggingBlockId(blockId); setDraggingBlockId(blockId);
}; };
const handleBlockDragEnd = () => {
isDraggingBlockRef.current = false;
setDraggingBlockId(null);
setDragOverBlockTarget(null);
if (idleUnlockTimerRef.current) {
clearTimeout(idleUnlockTimerRef.current);
}
idleUnlockTimerRef.current = setTimeout(async () => {
if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) {
await flushPendingSave();
await releaseMyLock();
}
}, 2000);
};
const handleBlockDragOver = (e: React.DragEvent, targetBlockId: string) => { const handleBlockDragOver = (e: React.DragEvent, targetBlockId: string) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@@ -3596,25 +3616,11 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
clearTimeout(idleUnlockTimerRef.current); clearTimeout(idleUnlockTimerRef.current);
idleUnlockTimerRef.current = null; idleUnlockTimerRef.current = null;
} }
isDraggingBlockRef.current = true; ensureLock(true);
ensureLock();
} }
}} }}
onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)} onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)}
onDragEnd={() => { onDragEnd={handleBlockDragEnd}
isDraggingBlockRef.current = false;
setDraggingBlockId(null);
setDragOverBlockTarget(null);
if (idleUnlockTimerRef.current) {
clearTimeout(idleUnlockTimerRef.current);
}
idleUnlockTimerRef.current = setTimeout(async () => {
if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) {
await flushPendingSave();
await releaseMyLock();
}
}, 2500);
}}
className="opacity-0 group-hover:opacity-100 flex items-center pt-1 text-faint hover:text-accent cursor-grab active:cursor-grabbing flex-shrink-0 -ml-1 mr-0.5" className="opacity-0 group-hover:opacity-100 flex items-center pt-1 text-faint hover:text-accent cursor-grab active:cursor-grabbing flex-shrink-0 -ml-1 mr-0.5"
title="Ziehen zum Verschieben" title="Ziehen zum Verschieben"
> >
@@ -3648,25 +3654,11 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
clearTimeout(idleUnlockTimerRef.current); clearTimeout(idleUnlockTimerRef.current);
idleUnlockTimerRef.current = null; idleUnlockTimerRef.current = null;
} }
isDraggingBlockRef.current = true; ensureLock(true);
ensureLock();
} }
}} }}
onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)} onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)}
onDragEnd={() => { onDragEnd={handleBlockDragEnd}
isDraggingBlockRef.current = false;
setDraggingBlockId(null);
setDragOverBlockTarget(null);
if (idleUnlockTimerRef.current) {
clearTimeout(idleUnlockTimerRef.current);
}
idleUnlockTimerRef.current = setTimeout(async () => {
if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) {
await flushPendingSave();
await releaseMyLock();
}
}, 2500);
}}
onDragOver={(e) => { onDragOver={(e) => {
if (!isReadOnly && draggingBlockId && draggingBlockId !== block.id) { if (!isReadOnly && draggingBlockId && draggingBlockId !== block.id) {
e.preventDefault(); e.preventDefault();