Skip to content

Conversation

ffischbach-ino
Copy link
Member

Description

This feature implements real-time card locking to prevent multiple users from simultaneously dragging the same card, eliminating conflicting drag operations. When a user starts dragging a card, other users see visual indicators showing who is currently moving it and are prevented from interfering with the drag operation.

Changelog

Added real-time collaborative drag locking system that displays user avatars and names on cards being moved by other participants, preventing drag conflicts and improving the collaborative editing experience. The system includes WebSocket-based event broadcasting, Redux state management, and comprehensive visual feedback with proper error handling for network failures

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • The light- and dark-theme are both supported and tested
  • The design was implemented and is responsive for all devices and screen sizes
  • The application was tested in the most commonly used browsers (e.g. Chrome, Firefox, Safari)

(Optional) Visual Changes

Bildschirmaufnahme.2025-09-15.um.12.55.58.mov

Prevent multiple users from simultaneously dragging the same card by implementing
a real-time locking system with visual feedback.

- Add NOTE_DRAG_START and NOTE_DRAG_END WebSocket events
- Create dragLocks Redux slice for state management
- Add API endpoints for broadcasting drag events
- Implement visual indicators showing which user is dragging
- Display user avatar and name on locked cards
- Disable drag interactions on locked cards
- Add proper styling with DragLockIndicator.scss

This prevents conflicting drag operations and provides clear visual feedback
about who is currently moving each card.
Extract remaining inline styles from Sortable.tsx to Sortable.scss for better
maintainability and separation of concerns. All styling is now properly
organized in CSS files with BEM methodology.
Revert shouldCombine class name to maintain compatibility with existing
Note.scss styling that depends on .shouldCombine selector for visual
feedback during note stacking operations.
Remove console.log and console.warn statements from drag lock
implementation to resolve ESLint warnings while maintaining
silent error handling for better user experience.
@ffischbach-ino ffischbach-ino force-pushed the ff/disable-note-moving-for-others-while-dragging-#2891 branch from 0bb8014 to c2942e0 Compare September 15, 2025 11:13
Copy link

The deployment to the dev cluster was successful. You can find the deployment here: https://5410.development.scrumlr.fra.ics.inovex.io
This deployment is only for testing purposes and will be deleted after 1 week.
To redeploy rerun the workflow.
DO NOT STORE IMPORTANT DATA ON THIS DEPLOYMENT

Deployed Images
  • ghcr.io/inovex/scrumlr.io/scrumlr-frontend:sha-4db0cc8

  • ghcr.io/inovex/scrumlr.io/scrumlr-server:sha-4db0cc8

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two questions that came to mind:

  1. is REST suitable for this kind of feature? sounds like potential race conditions which could arise
  2. I think this should be the same route with information whether it was drag start or end in the body. this is because we are essentially modifying the same resource. also, I think PUT could be more appropriate since were not creating a new ressource.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points about the race conditions - that's exactly why I think REST isn't ideal here.

You're right about the unified endpoint and PUT being more appropriate. But since we're dealing with real-time drag state, I'm leaning towards using WebSockets bidirectionally instead of our usual one-way approach (server → client).

We've always only sent updates to clients via WebSocket, but for this feature it might make sense to also receive drag events from clients the same way. Would eliminate the race condition issues.

I've drafted the bidirectional WebSocket implementation already. Want to discuss this with the backend team first though - it's a bit of an architectural shift.

@Schwehn42 Schwehn42 added the Changes Requested Changes requested by the reviewer label Sep 16, 2025
- Implement in-memory drag lock management with automatic cleanup
- Add configurable timeout and cleanup intervals with sensible defaults
- Support concurrent access with RWMutex for thread safety
- Include background cleanup goroutine for expired locks
- Provide dependency injection interface for testability
- Create drag_locks table with note_id as primary key
- Add foreign key constraints to notes, users, and boards
- Include indexes for performance optimization
- Support atomic lock operations across multiple pods
- Add PostgreSQL-based drag lock management with raw SQL
- Support atomic lock acquisition using INSERT ON CONFLICT
- Include automatic cleanup of expired locks (30 seconds)
- Broadcast lock events via NATS for multi-pod coordination
- Follow established patterns from reactions service
- Remove in-memory implementation from service.go
- Keep only interface and factory function
- Configure main.go to use database service
- Enable multi-pod drag lock coordination
- Replace separate drag-start/drag-end endpoints with unified drag-state
- Implement lock acquisition with HTTP 409 conflict responses
- Integrate with drag lock service for coordination
- Broadcast realtime events for drag state changes
- Add updateNoteDragState API method with boolean drag state
- Update Redux thunks to use new unified endpoint
- Maintain backward compatibility with existing drag thunks
- Prepare for enhanced error handling and conflict detection
- Remove created_at index as table will have minimal rows (~50-100)
- Keep board_id index for GetLocksForBoard queries
- Reduce index maintenance overhead for short-lived locks
- Add drag_locks table to track note dragging state
- Include foreign key constraints for data integrity
- Add proper indexes for performance optimization
- Add WebSocket message handling for drag lock operations
- Implement acquire and release lock functionality
- Add automatic lock cleanup on user disconnect
- Include comprehensive test coverage for database operations
- Add WebSocket message routing for drag lock operations
- Implement automatic lock release on client disconnect
- Update board listener to handle incoming drag lock messages
- Import necessary drag locks dependencies in router
- Replace hardcoded values with design system constants
- Import style constants for consistent spacing and colors
- Use semantic color variables for better maintainability
- Use non-null assertions for state selectors where safe to assume data exists
- Fix dragDisabled type from boolean | '' to boolean using Boolean()
- Simplify draggingUser lookup logic for better readability
- Extract allParticipants variable to improve code clarity
- Replace raw SQL queries with type-safe Bun operations
- Add DatabaseDragLock and DatabaseDragLockInsert models
- Update service initialization to use bun.DB instead of sql.DB
- Migrate all CRUD operations (AcquireLock, ReleaseLock, IsLocked, GetLocksForBoard)
- Convert cleanup operations to use Bun delete queries
- Update tests to use Bun operations and disable FK constraints for isolation
- Improve type safety and maintainability while preserving functionality

BREAKING CHANGE: drag lock service now requires bun.DB instead of sql.DB
- Remove updateNoteDragState endpoint from notes API
- Fix code formatting and indentation in notes.go
- Improve drag lock release logic with fallback deletion
- Handle race conditions in lock release operations
- Remove HTTP-based updateNoteDragState API function
- Replace HTTP calls with WebSocket messages for drag state
- Improve participant lookup logic in Sortable component
- Export sendWebSocketMessage function for drag lock communication
- Use DRAG_LOCK_MESSAGE type with ACQUIRE/RELEASE actions
- Add comprehensive test coverage for WebSocket drag lock messages
- Test ACQUIRE and RELEASE actions with various scenarios
- Include tests for invalid message formats and edge cases
…ging-#2891

# Conflicts:
#	server/src/api/boards_listen_on_board.go
#	server/src/api/notes.go
#	server/src/api/router.go
#	server/src/main.go
- Add missing context parameter to BroadcastToBoard calls in drag locks
- Add handleWebSocketMessage function to route drag lock WebSocket messages
- Integrate drag lock message handling in board WebSocket connection
- Release user drag locks when WebSocket connection closes
- Update mock Publish method to expect 3 parameters instead of 2
- Fixes test failures due to updated realtime broker interface
- Affects database service and websocket handler test suites
- Add nil dragLocks parameter to New() function call in board templates test
- Fixes test compilation error due to updated router signature
- Required for API integration tests to pass
Update sendWebSocketMessage parameter type from 'any' to 'string' to improve type safety and align with WebSocket API expectations.
Add DragLockMessage interface and ClientMessage union type to support
type-safe client-to-server WebSocket messaging for drag lock operations.
Update sendWebSocketMessage function to accept ClientMessage type
instead of generic string parameter, ensuring type safety for
WebSocket client messages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changes Requested Changes requested by the reviewer
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants