feat(workspace): activate write lock immediately on bullet drag-and-drop start
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s

This commit is contained in:
sandrodach committed 2026-10-25 19:33:25 +01:00
1 parent 09725611b5
commit 0008e9c420
1 file changed
+73 -2
@@ -198,6 +198,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
const idleUnlockTimerRef = useRef<any>(null);
const currentDocIdRef = useRef<string | null>(null);
const isEditorFocusedRef = useRef(false);
const isDraggingBlockRef = useRef(false);
const blockInputRefs = useRef<Record<string, HTMLTextAreaElement | HTMLInputElement | null>>({});
@@ -339,8 +340,15 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
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<WorkspaceViewProps> = ({
// 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<WorkspaceViewProps> = ({
draggingBlockId;
const position = explicitPosition || dragOverBlockTarget?.position || 'after';
isDraggingBlockRef.current = false;
setDraggingBlockId(null);
setDragOverBlockTarget(null);
@@ -2054,6 +2075,16 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
}
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<WorkspaceViewProps> = ({
{/* Drag Handle on hover - tightened spacing to the bullet */}
<div
draggable={!isReadOnly}
onMouseDown={() => {
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<WorkspaceViewProps> = ({
{/* Interactive Bullet Dot (Click: Zoom-in! Drag: Reorder! Drop on it: Nest as child!) */}
<div
draggable={!isReadOnly}
onMouseDown={() => {
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) {