diff --git a/lib/features/sheet_viewer/drawing/drawing_controller.dart b/lib/features/sheet_viewer/drawing/drawing_controller.dart index c5ee502..1dad711 100644 --- a/lib/features/sheet_viewer/drawing/drawing_controller.dart +++ b/lib/features/sheet_viewer/drawing/drawing_controller.dart @@ -51,6 +51,9 @@ class DrawingController extends ChangeNotifier { /// Maximum number of history steps to keep final int maxHistorySteps; + /// Whether there are unsaved changes since last load/clear + bool _hasUnsavedChanges = false; + DrawingController({this.maxHistorySteps = 50}); // --------------------------------------------------------------------------- @@ -75,6 +78,9 @@ class DrawingController extends ChangeNotifier { /// Whether redo is available bool get canRedo => _redoStack.isNotEmpty; + /// Whether there are unsaved changes since last load/clear/markSaved + bool get hasUnsavedChanges => _hasUnsavedChanges; + // --------------------------------------------------------------------------- // Drawing Operations // --------------------------------------------------------------------------- @@ -120,6 +126,7 @@ class DrawingController extends ChangeNotifier { _redoStack.clear(); _trimHistory(); _currentErasedLines.clear(); + _hasUnsavedChanges = true; notifyListeners(); // Update UI to enable undo button } return; @@ -133,6 +140,7 @@ class DrawingController extends ChangeNotifier { _undoStack.add(AddLineAction(_currentLine!)); _redoStack.clear(); _trimHistory(); + _hasUnsavedChanges = true; } _currentLine = null; @@ -257,6 +265,7 @@ class DrawingController extends ChangeNotifier { _redoStack.add(action); } + _hasUnsavedChanges = true; notifyListeners(); } @@ -279,6 +288,7 @@ class DrawingController extends ChangeNotifier { _undoStack.add(action); } + _hasUnsavedChanges = true; notifyListeners(); } @@ -289,6 +299,7 @@ class DrawingController extends ChangeNotifier { _redoStack.clear(); _currentLine = null; _currentErasedLines.clear(); + _hasUnsavedChanges = false; notifyListeners(); } @@ -324,6 +335,7 @@ class DrawingController extends ChangeNotifier { _redoStack.clear(); _currentLine = null; _currentErasedLines.clear(); + _hasUnsavedChanges = false; for (final json in jsonList) { _lines.add(DrawingLine.fromJson(json)); @@ -354,6 +366,11 @@ class DrawingController extends ChangeNotifier { notifyListeners(); } + /// Marks the current state as saved (resets unsaved changes flag). + void markSaved() { + _hasUnsavedChanges = false; + } + @override void dispose() { _lines.clear(); diff --git a/lib/features/sheet_viewer/sheet_viewer_page.dart b/lib/features/sheet_viewer/sheet_viewer_page.dart index d11d99a..b2f26a3 100644 --- a/lib/features/sheet_viewer/sheet_viewer_page.dart +++ b/lib/features/sheet_viewer/sheet_viewer_page.dart @@ -103,7 +103,7 @@ class _SheetViewerPageState extends State }); // Sync annotations from server (downloads newer versions) - await _syncService.syncAnnotationsFromServer(widget.sheet.uuid); + await _syncService.syncFromServer(widget.sheet.uuid); // Load annotations for current page(s) await _loadAnnotationsForCurrentPages(); @@ -143,32 +143,40 @@ class _SheetViewerPageState extends State } /// Saves the current page(s) annotations to storage and uploads to server. + /// + /// Only saves if there are actual changes to avoid unnecessary writes/uploads. Future _saveCurrentAnnotations() async { final now = DateTime.now(); - // Save left page - final leftJson = _leftDrawingController.toJsonString(); - final leftHasContent = leftJson.isNotEmpty && leftJson != '[]'; + // Save left page only if changed + if (_leftDrawingController.hasUnsavedChanges) { + final leftJson = _leftDrawingController.toJsonString(); + final leftHasContent = leftJson.isNotEmpty && leftJson != '[]'; - await _storageService.writeAnnotationsWithMetadata( - widget.sheet.uuid, - _currentPage, - leftHasContent ? leftJson : null, - now, - ); - - // Upload left page to server - if (leftHasContent) { - _syncService.uploadAnnotation( - sheetUuid: widget.sheet.uuid, - page: _currentPage, - annotationsJson: leftJson, - lastModified: now, + await _storageService.writeAnnotationsWithMetadata( + widget.sheet.uuid, + _currentPage, + leftHasContent ? leftJson : null, + now, ); + + // Upload left page to server + if (leftHasContent) { + _syncService.uploadAnnotation( + sheetUuid: widget.sheet.uuid, + page: _currentPage, + annotationsJson: leftJson, + lastModified: now, + ); + } + + _leftDrawingController.markSaved(); } - // Save right page (two-page mode) - if (widget.config.twoPageMode && _currentPage < _totalPages) { + // Save right page (two-page mode) only if changed + if (widget.config.twoPageMode && + _currentPage < _totalPages && + _rightDrawingController.hasUnsavedChanges) { final rightJson = _rightDrawingController.toJsonString(); final rightHasContent = rightJson.isNotEmpty && rightJson != '[]'; @@ -188,6 +196,8 @@ class _SheetViewerPageState extends State lastModified: now, ); } + + _rightDrawingController.markSaved(); } } @@ -290,9 +300,8 @@ class _SheetViewerPageState extends State icon: Icon( widget.config.fullscreen ? Icons.fullscreen_exit : Icons.fullscreen, ), - tooltip: widget.config.fullscreen - ? 'Exit Fullscreen' - : 'Enter Fullscreen', + tooltip: + widget.config.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen', onPressed: _toggleFullscreen, ), IconButton( @@ -304,9 +313,8 @@ class _SheetViewerPageState extends State icon: Icon( widget.config.twoPageMode ? Icons.filter_1 : Icons.filter_2, ), - tooltip: widget.config.twoPageMode - ? 'Single Page Mode' - : 'Two Page Mode', + tooltip: + widget.config.twoPageMode ? 'Single Page Mode' : 'Two Page Mode', onPressed: _toggleTwoPageMode, ), ], @@ -338,9 +346,8 @@ class _SheetViewerPageState extends State currentPageNumber: _currentPage, config: widget.config, leftDrawingController: _leftDrawingController, - rightDrawingController: widget.config.twoPageMode - ? _rightDrawingController - : null, + rightDrawingController: + widget.config.twoPageMode ? _rightDrawingController : null, drawingEnabled: _isPaintMode, );