Implement offline mode

This commit is contained in:
2026-02-06 16:41:58 +01:00
parent 58157a2e6e
commit d0fd96a2f5
7 changed files with 556 additions and 31 deletions

View File

@@ -6,6 +6,7 @@ import 'package:sheetless/core/models/config.dart';
import 'package:sheetless/core/models/sheet.dart';
import 'package:sheetless/core/services/api_client.dart';
import 'package:sheetless/core/services/storage_service.dart';
import 'package:sheetless/core/services/sync_service.dart';
import '../../app.dart';
import '../auth/login_page.dart';
@@ -34,8 +35,11 @@ class _HomePageState extends State<HomePage> with RouteAware {
final _storageService = StorageService();
ApiClient? _apiClient;
late Future<List<Sheet>> _sheetsFuture;
SyncService? _syncService;
late Future<SyncResult> _syncFuture;
List<Sheet> _sheets = [];
bool _isShuffling = false;
bool _isOnline = true;
String? _appName;
String? _appVersion;
@@ -52,7 +56,7 @@ class _HomePageState extends State<HomePage> with RouteAware {
});
_loadAppInfo();
_sheetsFuture = _loadSheets();
_syncFuture = _loadSheets();
}
@override
@@ -90,19 +94,29 @@ class _HomePageState extends State<HomePage> with RouteAware {
});
}
Future<List<Sheet>> _loadSheets() async {
Future<SyncResult> _loadSheets() async {
final url = await _storageService.readSecure(SecureStorageKey.url);
final jwt = await _storageService.readSecure(SecureStorageKey.jwt);
_apiClient = ApiClient(baseUrl: url!, token: jwt);
_syncService = SyncService(
apiClient: _apiClient!,
storageService: _storageService,
);
final sheets = await _apiClient!.fetchSheets();
_log.info('${sheets.length} sheets fetched');
// Perform sync (fetches sheets, uploads pending changes/annotations)
final result = await _syncService!.sync();
_log.info(
'${result.sheets.length} sheets loaded (online: ${result.isOnline}, '
'changes synced: ${result.changesSynced}, '
'annotations synced: ${result.annotationsSynced})',
);
final sortedSheets = await _sortSheetsByRecency(sheets);
_log.info('${sortedSheets.length} sheets sorted');
// Sort and store sheets
_sheets = await _sortSheetsByRecency(result.sheets);
_isOnline = result.isOnline;
return sortedSheets;
return result;
}
Future<List<Sheet>> _sortSheetsByRecency(List<Sheet> sheets) async {
@@ -128,7 +142,7 @@ class _HomePageState extends State<HomePage> with RouteAware {
Future<void> _refreshSheets() async {
setState(() {
_sheetsFuture = _loadSheets();
_syncFuture = _loadSheets();
});
}
@@ -137,12 +151,10 @@ class _HomePageState extends State<HomePage> with RouteAware {
// ---------------------------------------------------------------------------
void _handleShuffleChanged(bool enabled) async {
final sheets = await _sheetsFuture;
if (enabled) {
sheets.shuffle();
_sheets.shuffle();
} else {
await _sortSheetsByRecency(sheets);
await _sortSheetsByRecency(_sheets);
}
setState(() => _isShuffling = enabled);
@@ -181,7 +193,16 @@ class _HomePageState extends State<HomePage> with RouteAware {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sheetless')),
appBar: AppBar(
title: const Text('Sheetless'),
actions: [
if (!_isOnline)
const Padding(
padding: EdgeInsets.only(right: 8),
child: Icon(Icons.cloud_off, color: Colors.orange),
),
],
),
endDrawer: AppDrawer(
isShuffling: _isShuffling,
onShuffleChanged: _handleShuffleChanged,
@@ -194,8 +215,8 @@ class _HomePageState extends State<HomePage> with RouteAware {
}
Widget _buildBody() {
return FutureBuilder<List<Sheet>>(
future: _sheetsFuture,
return FutureBuilder<SyncResult>(
future: _syncFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
@@ -208,8 +229,9 @@ class _HomePageState extends State<HomePage> with RouteAware {
if (snapshot.hasData) {
return SheetsList(
sheets: snapshot.data!,
sheets: _sheets,
onSheetSelected: _openSheet,
syncService: _syncService!,
);
}