diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx index f124ec3..283ce20 100644 --- a/frontend/src/features/workspace/WorkspaceView.tsx +++ b/frontend/src/features/workspace/WorkspaceView.tsx @@ -198,6 +198,7 @@ export const WorkspaceView: React.FC = ({ const idleUnlockTimerRef = useRef(null); const currentDocIdRef = useRef(null); const isEditorFocusedRef = useRef(false); + const isDraggingBlockRef = useRef(false); const blockInputRefs = useRef>({}); @@ -339,8 +340,15 @@ export const WorkspaceView: React.FC = ({ pendingSaveDocRef.current = null; setSaveState('saved'); } - if (!isEditorFocusedRef.current && holdsLockRef.current) { - releaseMyLock(); + if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) { + if (idleUnlockTimerRef.current) { + clearTimeout(idleUnlockTimerRef.current); + } + idleUnlockTimerRef.current = setTimeout(async () => { + if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) { + await releaseMyLock(); + } + }, 2000); } } }; @@ -1940,6 +1948,18 @@ export const WorkspaceView: React.FC = ({ // Drag & Drop Handlers on the Bullet row / Grip / Dot const handleBlockDragStart = (e: React.DragEvent, blockId: string) => { e.stopPropagation(); + if (isReadOnly) { + e.preventDefault(); + return; + } + isDraggingBlockRef.current = true; + if (idleUnlockTimerRef.current) { + clearTimeout(idleUnlockTimerRef.current); + idleUnlockTimerRef.current = null; + } + // Activate write lock immediately upon dragging + ensureLock(); + e.dataTransfer.setData('application/mindsift-block-id', blockId); e.dataTransfer.setData('text/plain', blockId); e.dataTransfer.effectAllowed = 'move'; @@ -1990,6 +2010,7 @@ export const WorkspaceView: React.FC = ({ draggingBlockId; const position = explicitPosition || dragOverBlockTarget?.position || 'after'; + isDraggingBlockRef.current = false; setDraggingBlockId(null); setDragOverBlockTarget(null); @@ -2054,6 +2075,16 @@ export const WorkspaceView: React.FC = ({ } persistDocument({ ...selectedDoc, blocks }, true); + + if (idleUnlockTimerRef.current) { + clearTimeout(idleUnlockTimerRef.current); + } + idleUnlockTimerRef.current = setTimeout(async () => { + if (!isEditorFocusedRef.current && !isDraggingBlockRef.current && holdsLockRef.current) { + await flushPendingSave(); + await releaseMyLock(); + } + }, 2500); }; // Export full document as portable Logseq-indented Markdown @@ -3559,10 +3590,30 @@ export const WorkspaceView: React.FC = ({ {/* Drag Handle on hover - tightened spacing to the bullet */}
{ + if (!isReadOnly) { + if (idleUnlockTimerRef.current) { + clearTimeout(idleUnlockTimerRef.current); + idleUnlockTimerRef.current = null; + } + isDraggingBlockRef.current = true; + ensureLock(); + } + }} onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)} onDragEnd={() => { + 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" title="Ziehen zum Verschieben" @@ -3591,10 +3642,30 @@ export const WorkspaceView: React.FC = ({ {/* Interactive Bullet Dot (Click: Zoom-in! Drag: Reorder! Drop on it: Nest as child!) */}
{ + if (!isReadOnly) { + if (idleUnlockTimerRef.current) { + clearTimeout(idleUnlockTimerRef.current); + idleUnlockTimerRef.current = null; + } + isDraggingBlockRef.current = true; + ensureLock(); + } + }} onDragStart={(e) => !isReadOnly && handleBlockDragStart(e, block.id)} onDragEnd={() => { + 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) => { if (!isReadOnly && draggingBlockId && draggingBlockId !== block.id) {