Add sync status to app drawer

This commit is contained in:
2026-02-06 20:43:26 +01:00
parent 7ec7b20e74
commit 9261ec341e
3 changed files with 67 additions and 13 deletions

View File

@@ -11,12 +11,16 @@ class SyncResult {
final bool isOnline; final bool isOnline;
final int changesSynced; final int changesSynced;
final int annotationsSynced; final int annotationsSynced;
final int changesUnsynced;
final int annotationsUnsynced;
SyncResult({ SyncResult({
required this.sheets, required this.sheets,
required this.isOnline, required this.isOnline,
this.changesSynced = 0, required this.changesSynced,
this.annotationsSynced = 0, required this.annotationsSynced,
required this.changesUnsynced,
required this.annotationsUnsynced,
}); });
} }
@@ -35,8 +39,8 @@ class SyncService {
SyncService({ SyncService({
required ApiClient apiClient, required ApiClient apiClient,
required StorageService storageService, required StorageService storageService,
}) : _apiClient = apiClient, }) : _apiClient = apiClient,
_storageService = storageService; _storageService = storageService;
/// Performs a full sync operation. /// Performs a full sync operation.
/// ///
@@ -80,6 +84,8 @@ class SyncService {
// 3. Upload pending annotations // 3. Upload pending annotations
annotationsSynced = await _uploadPendingAnnotations(); annotationsSynced = await _uploadPendingAnnotations();
final remainingAnnotations = await _storageService
.readPendingAnnotationUploads();
// 4. Apply any remaining local changes (in case some failed to upload) // 4. Apply any remaining local changes (in case some failed to upload)
final changeQueue = await _storageService.readChangeQueue(); final changeQueue = await _storageService.readChangeQueue();
@@ -102,6 +108,8 @@ class SyncService {
isOnline: true, isOnline: true,
changesSynced: changesSynced, changesSynced: changesSynced,
annotationsSynced: annotationsSynced, annotationsSynced: annotationsSynced,
changesUnsynced: changeQueue.length,
annotationsUnsynced: remainingAnnotations.length,
); );
} }
@@ -127,9 +135,16 @@ class SyncService {
} }
} }
final remainingAnnotations = await _storageService
.readPendingAnnotationUploads();
return SyncResult( return SyncResult(
sheets: sheets, sheets: sheets,
isOnline: false, isOnline: false,
changesSynced: 0,
annotationsSynced: 0,
changesUnsynced: changeQueue.length,
annotationsUnsynced: remainingAnnotations.length,
); );
} }

View File

@@ -219,6 +219,7 @@ class _HomePageState extends State<HomePage> with RouteAware {
onLogout: _handleLogout, onLogout: _handleLogout,
appName: _appName, appName: _appName,
appVersion: _appVersion, appVersion: _appVersion,
syncFuture: _syncFuture,
), ),
body: RefreshIndicator(onRefresh: _refreshSheets, child: _buildBody()), body: RefreshIndicator(onRefresh: _refreshSheets, child: _buildBody()),
); );

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sheetless/core/services/sync_service.dart';
/// Callback for shuffle state changes. /// Callback for shuffle state changes.
typedef ShuffleCallback = void Function(bool enabled); typedef ShuffleCallback = void Function(bool enabled);
@@ -12,12 +13,14 @@ class AppDrawer extends StatelessWidget {
final VoidCallback onLogout; final VoidCallback onLogout;
final String? appName; final String? appName;
final String? appVersion; final String? appVersion;
final Future<SyncResult> syncFuture;
const AppDrawer({ const AppDrawer({
super.key, super.key,
required this.isShuffling, required this.isShuffling,
required this.onShuffleChanged, required this.onShuffleChanged,
required this.onLogout, required this.onLogout,
required this.syncFuture,
this.appName, this.appName,
this.appVersion, this.appVersion,
}); });
@@ -32,7 +35,7 @@ class AppDrawer extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_buildActions(), _buildActions(),
_buildAppInfo(), Column(children: [_buildSyncStatus(), _buildAppInfo()]),
], ],
), ),
), ),
@@ -44,10 +47,7 @@ class AppDrawer extends StatelessWidget {
return Column( return Column(
children: [ children: [
ListTile( ListTile(
leading: Icon( leading: Icon(Icons.shuffle, color: isShuffling ? Colors.blue : null),
Icons.shuffle,
color: isShuffling ? Colors.blue : null,
),
title: const Text('Shuffle'), title: const Text('Shuffle'),
onTap: () => onShuffleChanged(!isShuffling), onTap: () => onShuffleChanged(!isShuffling),
), ),
@@ -60,6 +60,47 @@ class AppDrawer extends StatelessWidget {
); );
} }
Widget _buildSyncStatus() {
return Center(
// padding: const EdgeInsets.all(5.0),
child: FutureBuilder<SyncResult>(
future: syncFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text(
"Error: ${snapshot.error.toString()}",
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
);
}
if (snapshot.hasData) {
final changes = snapshot.data!.changesUnsynced;
final annotations = snapshot.data!.annotationsUnsynced;
if (changes == 0 && annotations == 0) {
return Text(
"All synced!",
style: const TextStyle(color: Colors.black),
textAlign: TextAlign.center,
);
}
return Text(
"$changes changes and $annotations annotations unsynchronized!",
style: const TextStyle(color: Colors.red),
textAlign: TextAlign.center,
);
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
Widget _buildAppInfo() { Widget _buildAppInfo() {
final versionText = appName != null && appVersion != null final versionText = appName != null && appVersion != null
? '$appName v$appVersion' ? '$appName v$appVersion'
@@ -67,10 +108,7 @@ class AppDrawer extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(versionText, style: const TextStyle(color: Colors.grey)),
versionText,
style: const TextStyle(color: Colors.grey),
),
); );
} }
} }