sheets view: add refresh button for annotations
Build and deploy the latest docker container / deploy (push) Successful in 1m22s
Build and release the latest android apk / rolling-release-apk (push) Successful in 5m25s

This commit is contained in:
2026-07-04 11:26:04 +02:00
parent 984e32cd03
commit 416979ab3d
2 changed files with 44 additions and 9 deletions
@@ -15,24 +15,26 @@ class AnnotationSyncService {
AnnotationSyncService({ AnnotationSyncService({
required ApiClient apiClient, required ApiClient apiClient,
required StorageService storageService, required StorageService storageService,
}) : _apiClient = apiClient, }) : _apiClient = apiClient,
_storageService = storageService; _storageService = storageService;
/// Downloads annotations from server and merges with local storage. /// Downloads annotations from server and merges with local storage.
/// ///
/// For each page, compares server's lastModified with local lastModified. /// For each page, compares server's lastModified with local lastModified.
/// If server is newer, overwrites local. Local annotations that are newer /// If server is newer, overwrites local. Local annotations that are newer
/// are preserved. /// are preserved.
Future<void> syncFromServer(String sheetUuid) async { Future<void> syncAnnotationsFromServer(String sheetUuid) async {
try { try {
_log.info('Syncing annotations from server for sheet $sheetUuid'); _log.info('Syncing annotations from server for sheet $sheetUuid');
// Fetch all annotations from server // Fetch all annotations from server
final serverAnnotations = await _apiClient.fetchAnnotations(sheetUuid); final serverAnnotations = await _apiClient.fetchAnnotations(sheetUuid);
_log.fine('Fetched annotations from server for sheet $sheetUuid');
// Get all local annotations with metadata // Get all local annotations with metadata
final localAnnotations = final localAnnotations = await _storageService
await _storageService.readAllAnnotationsWithMetadata(sheetUuid); .readAllAnnotationsWithMetadata(sheetUuid);
int updatedCount = 0; int updatedCount = 0;
@@ -118,7 +120,8 @@ class AnnotationSyncService {
return false; return false;
} catch (e) { } catch (e) {
_log.warning( _log.warning(
'Unexpected error uploading annotation, queuing for later: $e'); 'Unexpected error uploading annotation, queuing for later: $e',
);
await _queueForLaterUpload( await _queueForLaterUpload(
sheetUuid: sheetUuid, sheetUuid: sheetUuid,
page: page, page: page,
@@ -40,6 +40,7 @@ class _SheetViewerPageState extends State<SheetViewerPage>
PdfDocument? _document; PdfDocument? _document;
late Future<bool> _documentLoaded; late Future<bool> _documentLoaded;
late Future<void> _annotationsLoaded;
int _currentPage = 1; int _currentPage = 1;
int _totalPages = 1; int _totalPages = 1;
bool _isPaintMode = false; bool _isPaintMode = false;
@@ -69,6 +70,15 @@ class _SheetViewerPageState extends State<SheetViewerPage>
FullScreen.setFullScreen(widget.config.fullscreen); FullScreen.setFullScreen(widget.config.fullscreen);
} }
_documentLoaded = _loadPdf(); _documentLoaded = _loadPdf();
_syncAnnotations();
}
void _syncAnnotations() {
setState(() {
_annotationsLoaded = _syncService.syncAnnotationsFromServer(
widget.sheet.uuid,
);
});
} }
@override @override
@@ -97,20 +107,22 @@ class _SheetViewerPageState extends State<SheetViewerPage>
} else { } else {
// Native: use file cache // Native: use file cache
final file = await widget.apiClient.getPdfFileCached(widget.sheet.uuid); final file = await widget.apiClient.getPdfFileCached(widget.sheet.uuid);
_log.fine("Found file, opening...");
_document = await PdfDocument.openFile(file.path); _document = await PdfDocument.openFile(file.path);
widget.sheet.availableOffline = true; widget.sheet.availableOffline = true;
} }
_log.fine("Pdf loaded");
setState(() { setState(() {
_totalPages = _document!.pages.length; _totalPages = _document!.pages.length;
}); });
// Sync annotations from server (downloads newer versions)
await _syncService.syncFromServer(widget.sheet.uuid);
// Load annotations for current page(s) // Load annotations for current page(s)
await _loadAnnotationsForCurrentPages(); await _loadAnnotationsForCurrentPages();
_log.fine("Annotations loaded");
return true; return true;
} }
@@ -304,6 +316,26 @@ class _SheetViewerPageState extends State<SheetViewerPage>
: 'Enter Fullscreen', : 'Enter Fullscreen',
onPressed: _toggleFullscreen, onPressed: _toggleFullscreen,
), ),
FutureBuilder<void>(
future: _annotationsLoaded,
builder: (context, snapshot) {
if (snapshot.hasError) {
_log.warning('Error loading annotations', snapshot.error);
return IconButton(
onPressed: _syncAnnotations,
icon: Icon(Icons.error, color: Colors.red),
);
}
if (snapshot.connectionState == ConnectionState.done) {
_loadAnnotationsForCurrentPages();
return IconButton(
onPressed: _syncAnnotations,
icon: Icon(Icons.refresh),
);
}
return const CircularProgressIndicator();
},
),
IconButton( IconButton(
icon: Icon(_isPaintMode ? Icons.brush : Icons.brush_outlined), icon: Icon(_isPaintMode ? Icons.brush : Icons.brush_outlined),
tooltip: _isPaintMode ? 'Exit Paint Mode' : 'Enter Paint Mode', tooltip: _isPaintMode ? 'Exit Paint Mode' : 'Enter Paint Mode',