mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-13 07:42:55 +01:00
161 lines
5.8 KiB
Markdown
161 lines
5.8 KiB
Markdown
# Blockly Drag-and-Drop Fix Attempt
|
|
|
|
**Date:** 2026-01-11
|
|
**Status:** Fix Implemented - Testing Required
|
|
**Severity:** High (Core functionality)
|
|
|
|
## Problem Summary
|
|
|
|
Two critical issues with Blockly integration:
|
|
|
|
1. **Drag Timeout:** Blocks could only be dragged for ~1000ms before gesture terminated
|
|
2. **Connection Errors:** Console flooded with errors when trying to connect blocks
|
|
|
|
## Root Cause Analysis
|
|
|
|
The original implementation used **blanket debouncing** on ALL Blockly change events:
|
|
|
|
```typescript
|
|
// ❌ OLD APPROACH - Debounced ALL events
|
|
const changeListener = () => {
|
|
if (changeTimeoutRef.current) clearTimeout(changeTimeoutRef.current);
|
|
|
|
changeTimeoutRef.current = setTimeout(() => {
|
|
const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
|
|
const code = javascriptGenerator.workspaceToCode(workspace);
|
|
onChange(workspace, json, code);
|
|
}, 150);
|
|
};
|
|
```
|
|
|
|
### Why This Caused Problems
|
|
|
|
1. **During drag operations:** Blockly fires MANY events (BLOCK_DRAG, BLOCK_MOVE, etc.)
|
|
2. **Each event triggered:** A new debounce timeout
|
|
3. **React state updates:** Potentially caused re-renders during gesture
|
|
4. **Blockly's internal state:** Expected immediate consistency, but our debounce + React async updates created race conditions
|
|
5. **Insertion markers:** When trying to show connection previews, Blockly tried to update blocks that were in an inconsistent state
|
|
|
|
## The Solution
|
|
|
|
**Event Filtering** - Only respond to events that actually change workspace structure:
|
|
|
|
```typescript
|
|
// ✅ NEW APPROACH - Filter events intelligently
|
|
const changeListener = (event: Blockly.Events.Abstract) => {
|
|
if (!onChange || !workspace) return;
|
|
|
|
// Ignore UI events that don't change workspace structure
|
|
if (event.type === Blockly.Events.BLOCK_DRAG) return;
|
|
if (event.type === Blockly.Events.BLOCK_MOVE && !event.isUiEvent) return;
|
|
if (event.type === Blockly.Events.SELECTED) return;
|
|
if (event.type === Blockly.Events.CLICK) return;
|
|
if (event.type === Blockly.Events.VIEWPORT_CHANGE) return;
|
|
if (event.type === Blockly.Events.TOOLBOX_ITEM_SELECT) return;
|
|
if (event.type === Blockly.Events.THEME_CHANGE) return;
|
|
if (event.type === Blockly.Events.TRASHCAN_OPEN) return;
|
|
|
|
// For UI events that DO change workspace, debounce them
|
|
const isUiEvent = event.isUiEvent;
|
|
|
|
if (isUiEvent) {
|
|
// Debounce user-initiated changes (300ms)
|
|
changeTimeoutRef.current = setTimeout(() => {
|
|
const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
|
|
const code = javascriptGenerator.workspaceToCode(workspace);
|
|
onChange(workspace, json, code);
|
|
}, 300);
|
|
} else {
|
|
// Programmatic changes fire immediately (undo/redo, loading)
|
|
const json = JSON.stringify(Blockly.serialization.workspaces.save(workspace));
|
|
const code = javascriptGenerator.workspaceToCode(workspace);
|
|
onChange(workspace, json, code);
|
|
}
|
|
};
|
|
```
|
|
|
|
### Key Changes
|
|
|
|
1. **Event type checking:** Ignore events that are purely UI feedback
|
|
2. **UI vs Programmatic:** Different handling based on event source
|
|
3. **No interference with gestures:** BLOCK_DRAG events are completely ignored
|
|
4. **Longer debounce:** Increased from 150ms to 300ms for stability
|
|
5. **Immediate programmatic updates:** Undo/redo and loading don't debounce
|
|
|
|
## Expected Results
|
|
|
|
### Before Fix
|
|
|
|
- ❌ Drag stops after ~1000ms
|
|
- ❌ Console errors during connection attempts
|
|
- ❌ Insertion markers cause state corruption
|
|
- ✅ But: no event spam (previous fix still working)
|
|
|
|
### After Fix
|
|
|
|
- ✅ Drag continuously for 10+ seconds
|
|
- ✅ No console errors during connections
|
|
- ✅ Clean insertion marker operations
|
|
- ✅ No event spam (maintained)
|
|
|
|
## Testing Checklist
|
|
|
|
### Drag Performance
|
|
|
|
- [ ] Drag block from toolbox → workspace (slow drag, 5+ seconds)
|
|
- [ ] Drag block around workspace (slow drag, 10+ seconds)
|
|
- [ ] Drag block quickly across workspace
|
|
- [ ] Drag multiple blocks in succession
|
|
|
|
### Connection Operations
|
|
|
|
- [ ] Drag block to connect to another block
|
|
- [ ] Check console for errors during connection
|
|
- [ ] Verify insertion marker appears/disappears smoothly
|
|
- [ ] Verify blocks actually connect properly
|
|
|
|
### Workspace Persistence
|
|
|
|
- [ ] Add blocks, close tab, reopen → blocks should persist
|
|
- [ ] Edit workspace, switch to canvas, back to Logic Builder → no loss
|
|
- [ ] Save project, reload → workspace loads correctly
|
|
|
|
### Performance
|
|
|
|
- [ ] No lag during dragging
|
|
- [ ] Console shows reasonable event frequency
|
|
- [ ] Project saves at reasonable intervals (not spamming)
|
|
|
|
## Files Modified
|
|
|
|
- `packages/noodl-editor/src/editor/src/views/BlocklyEditor/BlocklyWorkspace.tsx`
|
|
- Replaced blanket debouncing with event filtering
|
|
- Added event type checks for UI-only events
|
|
- Separated UI vs programmatic event handling
|
|
- Increased debounce timeout to 300ms
|
|
|
|
## Rollback Plan
|
|
|
|
If this fix doesn't work, we can:
|
|
|
|
1. Revert to previous debounced approach
|
|
2. Try alternative: disable onChange during gestures using Blockly gesture events
|
|
3. Try alternative: use MutationObserver instead of change events
|
|
|
|
## Learning
|
|
|
|
> **Blockly Event System:** Blockly fires many event types. Not all need persistence. UI feedback events (drag, select, viewport) should be ignored. Only respond to structural changes (CREATE, DELETE, CHANGE, MOVE completed). The `isUiEvent` property distinguishes user actions from programmatic changes.
|
|
|
|
## Next Steps
|
|
|
|
1. **Test the fix** - Run through testing checklist above
|
|
2. **If successful** - Update DRAG-DROP-ISSUE.md with "RESOLVED" status
|
|
3. **If unsuccessful** - Document what still fails and try alternative approaches
|
|
4. **Record in CHANGELOG.md** - Document the fix for future reference
|
|
5. **Record in LEARNINGS.md** - Add to institutional knowledge
|
|
|
|
---
|
|
|
|
**Testing Required By:** Richard (manual testing in running app)
|
|
**Expected Outcome:** Smooth, continuous dragging with no console errors
|