@@ -19,6 +19,7 @@ import 'package:cookethflow/features/models/canvas_models/objects/triangle_objec
1919import 'package:cookethflow/features/models/canvas_models/user_cursor.dart' ;
2020import 'package:cookethflow/features/models/workspace_model.dart' ;
2121import 'package:flutter/material.dart' ;
22+ import 'package:flutter_quill/flutter_quill.dart' ;
2223import 'package:supabase_flutter/supabase_flutter.dart' ;
2324import 'package:uuid/uuid.dart' ;
2425
@@ -52,10 +53,10 @@ class WorkspaceProvider extends StateHandler {
5253 static const double _defaultShapeSize = 100.0 ; // In canvas units
5354 static const double _handleRadius = 8.0 ; // In canvas units
5455 Color _currentWorkspaceColor = scaffoldColor;
55-
5656 TextEditingController _workspaceNameController = TextEditingController (
5757 text: 'Workspace Name' ,
5858 );
59+ QuillController _quillController = QuillController .basic ();
5960
6061 bool get isLoading => _isLoading;
6162 bool get isDrawerOpen => _isDrawerOpen;
@@ -77,6 +78,7 @@ class WorkspaceProvider extends StateHandler {
7778 SupabaseService get supabaseService => _supabaseService;
7879
7980 bool get hasSelectedTile => _selectedTileIndex != null ;
81+ QuillController get quillController => _quillController;
8082
8183 // --- Core Methods ---
8284
@@ -99,7 +101,9 @@ class WorkspaceProvider extends StateHandler {
99101 _currentlySelectedObjectId = null ; // Clear selected object
100102
101103 await _fetchCanvasObjects (); // Fetch objects for the new workspace
102- _setupRealtimeChannel (_currentWorkspace! .id); // Set up realtime for new workspace
104+ _setupRealtimeChannel (
105+ _currentWorkspace! .id,
106+ ); // Set up realtime for new workspace
103107
104108 notifyListeners ();
105109 }
@@ -134,41 +138,49 @@ class WorkspaceProvider extends StateHandler {
134138 void _setupRealtimeChannel (String workspaceId) {
135139 // Only subscribe if not already subscribed to this workspace
136140 if (_canvasChannel? .topic != Constants .channelName + ':$workspaceId ' ) {
137- _canvasChannel = _supabaseService.supabase
138- .channel (Constants .channelName + ':$workspaceId ' ) // Unique channel per workspace
139- .onBroadcast (
140- event: Constants .broadcastEventName,
141- callback: (payload) {
142- // Only process broadcasts if they belong to the current workspace
143- if (payload['workspace_id' ] == _currentWorkspace? .id) {
144- final cursor = UserCursor .fromJson (payload['cursor' ]);
145- _userCursors[cursor.id] = cursor;
146-
147- if (payload['object' ] != null ) {
148- final object = CanvasObject .fromJson (payload['object' ]);
149- _canvasObjects[object.id] = object;
150- }
151- notifyListeners ();
152- }
153- },
154- )
155- .subscribe ();
141+ _canvasChannel =
142+ _supabaseService.supabase
143+ .channel (
144+ Constants .channelName + ':$workspaceId ' ,
145+ ) // Unique channel per workspace
146+ .onBroadcast (
147+ event: Constants .broadcastEventName,
148+ callback: (payload) {
149+ // Only process broadcasts if they belong to the current workspace
150+ if (payload['workspace_id' ] == _currentWorkspace? .id) {
151+ final cursor = UserCursor .fromJson (payload['cursor' ]);
152+ _userCursors[cursor.id] = cursor;
153+
154+ if (payload['object' ] != null ) {
155+ final object = CanvasObject .fromJson (payload['object' ]);
156+ _canvasObjects[object.id] = object;
157+ }
158+ notifyListeners ();
159+ }
160+ },
161+ )
162+ .subscribe ();
156163 }
157164 }
158165
159166 // --- Realtime Sync ---
160167 // Now expects canvas coordinates for cursorPosition
161168 Future <void > syncCanvasObject (Offset cursorPosition) {
162169 final myCursor = UserCursor (position: cursorPosition, id: _myId);
163- if (_currentWorkspace == null || _canvasChannel == null ) return Future .value ();
170+ if (_currentWorkspace == null || _canvasChannel == null )
171+ return Future .value ();
164172
165173 return _canvasChannel! .sendBroadcastMessage (
166174 event: Constants .broadcastEventName,
167175 payload: {
168176 'cursor' : myCursor.toJson (),
169- if (_currentlySelectedObjectId != null && _canvasObjects.containsKey (_currentlySelectedObjectId! )) // Ensure object exists
177+ if (_currentlySelectedObjectId != null &&
178+ _canvasObjects.containsKey (
179+ _currentlySelectedObjectId! ,
180+ )) // Ensure object exists
170181 'object' : _canvasObjects[_currentlySelectedObjectId! ]! .toJson (),
171- 'workspace_id' : _currentWorkspace! .id, // Include workspace_id in broadcast
182+ 'workspace_id' :
183+ _currentWorkspace! .id, // Include workspace_id in broadcast
172184 },
173185 );
174186 }
@@ -178,8 +190,11 @@ class WorkspaceProvider extends StateHandler {
178190 // Debounce this if it causes too many updates on every key stroke
179191 // For simplicity, we'll directly update on change for now.
180192 // A better approach for frequent changes is to use a debounce timer.
181- if (_currentWorkspace != null && _currentWorkspace! .name != _workspaceNameController.text) {
182- _currentWorkspace = _currentWorkspace! .copyWith (name: _workspaceNameController.text);
193+ if (_currentWorkspace != null &&
194+ _currentWorkspace! .name != _workspaceNameController.text) {
195+ _currentWorkspace = _currentWorkspace! .copyWith (
196+ name: _workspaceNameController.text,
197+ );
183198 _updateWorkspaceNameInDb (_workspaceNameController.text);
184199 }
185200 }
@@ -209,7 +224,9 @@ class WorkspaceProvider extends StateHandler {
209224 // Save the current object state to the database
210225 await _supabaseService.supabase.from ('canvas_objects' ).upsert ({
211226 'id' : drawnObjectId,
212- 'object' : _canvasObjects[drawnObjectId]! .toJson (), // Ensure this toJson() matches your DB schema (jsonb)
227+ 'object' :
228+ _canvasObjects[drawnObjectId]!
229+ .toJson (), // Ensure this toJson() matches your DB schema (jsonb)
213230 'workspace_id' : _currentWorkspace! .id,
214231 });
215232 print ('Canvas object ${drawnObjectId } upserted to DB.' );
@@ -290,18 +307,26 @@ class WorkspaceProvider extends StateHandler {
290307 }
291308
292309 // Expects details.globalPosition to be in canvas coordinates
293- void addNewNode (DragDownDetails details) async { // Make async to save immediately
310+ void addNewNode (DragDownDetails details) async {
311+ // Make async to save immediately
294312 if (_currentWorkspace == null ) {
295313 print ("Cannot add node: No workspace selected." );
296314 return ;
297315 }
298316 CanvasObject ? newObject;
299- final defaultTopLeft = details.globalPosition - const Offset (_defaultShapeSize / 2 , _defaultShapeSize / 2 );
300- final defaultBottomRight = details.globalPosition + const Offset (_defaultShapeSize / 2 , _defaultShapeSize / 2 );
317+ final defaultTopLeft =
318+ details.globalPosition -
319+ const Offset (_defaultShapeSize / 2 , _defaultShapeSize / 2 );
320+ final defaultBottomRight =
321+ details.globalPosition +
322+ const Offset (_defaultShapeSize / 2 , _defaultShapeSize / 2 );
301323
302324 switch (_currentMode) {
303325 case DrawMode .circle:
304- newObject = Circle .createNew (details.globalPosition, _defaultShapeSize / 2 );
326+ newObject = Circle .createNew (
327+ details.globalPosition,
328+ _defaultShapeSize / 2 ,
329+ );
305330 break ;
306331 case DrawMode .rectangle:
307332 newObject = Rectangle .createNew (defaultTopLeft, defaultBottomRight);
@@ -325,7 +350,10 @@ class WorkspaceProvider extends StateHandler {
325350 newObject = Triangle .createNew (defaultTopLeft, defaultBottomRight);
326351 break ;
327352 case DrawMode .invertedTriangle:
328- newObject = InvertedTriangle .createNew (defaultTopLeft, defaultBottomRight);
353+ newObject = InvertedTriangle .createNew (
354+ defaultTopLeft,
355+ defaultBottomRight,
356+ );
329357 break ;
330358 case DrawMode .pointer:
331359 break ;
@@ -334,7 +362,8 @@ class WorkspaceProvider extends StateHandler {
334362 if (newObject != null ) {
335363 _canvasObjects[newObject.id] = newObject;
336364 _currentlySelectedObjectId = newObject.id;
337- _interactionMode = InteractionMode .moving; // Set to moving immediately after creation
365+ _interactionMode =
366+ InteractionMode .moving; // Set to moving immediately after creation
338367 notifyListeners ();
339368
340369 // Immediately save the newly created object to the database
@@ -353,8 +382,10 @@ class WorkspaceProvider extends StateHandler {
353382
354383 // Expects details.globalPosition and details.delta to be in canvas coordinates
355384 void onPanDown (DragDownDetails details) {
356- _cursorPosition = details.globalPosition; // This is now in canvas coordinates
357- _panStartPoint = details.globalPosition; // This is now in canvas coordinates
385+ _cursorPosition =
386+ details.globalPosition; // This is now in canvas coordinates
387+ _panStartPoint =
388+ details.globalPosition; // This is now in canvas coordinates
358389
359390 _currentlySelectedObjectId = null ;
360391 _interactionMode = InteractionMode .none;
@@ -371,17 +402,20 @@ class WorkspaceProvider extends StateHandler {
371402 _interactionMode = InteractionMode .resizingTopLeft;
372403 notifyListeners ();
373404 return ; // Exit after finding a handle
374- } else if ((details.globalPosition - rect.topRight).distance < _handleRadius) {
405+ } else if ((details.globalPosition - rect.topRight).distance <
406+ _handleRadius) {
375407 _currentlySelectedObjectId = canvasObject.id;
376408 _interactionMode = InteractionMode .resizingTopRight;
377409 notifyListeners ();
378410 return ;
379- } else if ((details.globalPosition - rect.bottomLeft).distance < _handleRadius) {
411+ } else if ((details.globalPosition - rect.bottomLeft).distance <
412+ _handleRadius) {
380413 _currentlySelectedObjectId = canvasObject.id;
381414 _interactionMode = InteractionMode .resizingBottomLeft;
382415 notifyListeners ();
383416 return ;
384- } else if ((details.globalPosition - rect.bottomRight).distance < _handleRadius) {
417+ } else if ((details.globalPosition - rect.bottomRight).distance <
418+ _handleRadius) {
385419 _currentlySelectedObjectId = canvasObject.id;
386420 _interactionMode = InteractionMode .resizingBottomRight;
387421 notifyListeners ();
@@ -391,7 +425,8 @@ class WorkspaceProvider extends StateHandler {
391425
392426 // If no handle interaction, check if we're clicking on an object to move it
393427 for (final canvasObject in _canvasObjects.values.toList ().reversed) {
394- if (canvasObject.intersectsWith (details.globalPosition)) { // details.globalPosition is now canvas coordinate
428+ if (canvasObject.intersectsWith (details.globalPosition)) {
429+ // details.globalPosition is now canvas coordinate
395430 _currentlySelectedObjectId = canvasObject.id;
396431 _interactionMode = InteractionMode .moving;
397432 notifyListeners ();
@@ -405,31 +440,49 @@ class WorkspaceProvider extends StateHandler {
405440
406441 // Expects details.globalPosition and details.delta to be in canvas coordinates
407442 void onPanUpdate (DragUpdateDetails details) {
408- _cursorPosition = details.globalPosition; // This is now in canvas coordinates
443+ _cursorPosition =
444+ details.globalPosition; // This is now in canvas coordinates
409445 if (_currentlySelectedObjectId == null ) return ;
410446
411447 final currentObject = _canvasObjects[_currentlySelectedObjectId! ];
412448 if (currentObject == null ) return ;
413449
414450 switch (_interactionMode) {
415451 case InteractionMode .moving:
416- _canvasObjects[_currentlySelectedObjectId! ] = currentObject.move (details.delta); // delta is already in canvas units
452+ _canvasObjects[_currentlySelectedObjectId! ] = currentObject.move (
453+ details.delta,
454+ ); // delta is already in canvas units
417455 break ;
418456 case InteractionMode .resizingTopLeft:
419457 final newTopLeft = currentObject.getBounds ().topLeft + details.delta;
420- _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic ).resize (newTopLeft, currentObject.getBounds ().bottomRight);
458+ _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic )
459+ .resize (newTopLeft, currentObject.getBounds ().bottomRight);
421460 break ;
422461 case InteractionMode .resizingTopRight:
423462 final newTopRight = currentObject.getBounds ().topRight + details.delta;
424- _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic ).resize (Offset (currentObject.getBounds ().topLeft.dx, newTopRight.dy), Offset (newTopRight.dx, currentObject.getBounds ().bottomRight.dy));
463+ _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic )
464+ .resize (
465+ Offset (currentObject.getBounds ().topLeft.dx, newTopRight.dy),
466+ Offset (newTopRight.dx, currentObject.getBounds ().bottomRight.dy),
467+ );
425468 break ;
426469 case InteractionMode .resizingBottomLeft:
427- final newBottomLeft = currentObject.getBounds ().bottomLeft + details.delta;
428- _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic ).resize (Offset (newBottomLeft.dx, currentObject.getBounds ().topLeft.dy), Offset (currentObject.getBounds ().bottomRight.dx, newBottomLeft.dy));
470+ final newBottomLeft =
471+ currentObject.getBounds ().bottomLeft + details.delta;
472+ _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic )
473+ .resize (
474+ Offset (newBottomLeft.dx, currentObject.getBounds ().topLeft.dy),
475+ Offset (
476+ currentObject.getBounds ().bottomRight.dx,
477+ newBottomLeft.dy,
478+ ),
479+ );
429480 break ;
430481 case InteractionMode .resizingBottomRight:
431- final newBottomRight = currentObject.getBounds ().bottomRight + details.delta;
432- _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic ).resize (currentObject.getBounds ().topLeft, newBottomRight);
482+ final newBottomRight =
483+ currentObject.getBounds ().bottomRight + details.delta;
484+ _canvasObjects[_currentlySelectedObjectId! ] = (currentObject as dynamic )
485+ .resize (currentObject.getBounds ().topLeft, newBottomRight);
433486 break ;
434487 case InteractionMode .none:
435488 break ;
@@ -457,4 +510,4 @@ class WorkspaceProvider extends StateHandler {
457510 _canvasChannel? .unsubscribe ();
458511 super .dispose ();
459512 }
460- }
513+ }
0 commit comments