import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_fullscreen/flutter_fullscreen.dart'; import 'package:logging/logging.dart'; import 'package:sheetless/login_page.dart'; import 'package:sheetless/sheet_viewer_page.dart'; import 'package:sheetless/storage_helper.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'api.dart'; import 'sheet.dart'; class MyHomePage extends StatefulWidget { final Config config; const MyHomePage({super.key, required this.config}); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State with FullScreenListener { ApiClient? apiClient; Future apiLoggedIn = Future.value(false); final StorageHelper _storageHelper = StorageHelper(); final log = Logger("MyHomePage"); String? appName; String? appVersion; bool shuffling = false; late Future> sheets; @override void initState() { FullScreen.addListener(this); // Load saved fullscreen FullScreen.setFullScreen(widget.config.fullscreen); super.initState(); _loadAppInfo(); sheets = acquireSheets(); } @override void dispose() { FullScreen.removeListener(this); super.dispose(); } Future _loadAppInfo() async { final info = await PackageInfo.fromPlatform(); setState(() { appName = info.appName; appVersion = info.version; }); } Future> acquireSheets() async { final url = await _storageHelper.readSecure(SecureStorageKey.url); final jwt = await _storageHelper.readSecure(SecureStorageKey.jwt); apiClient = ApiClient(baseUrl: url!, token: jwt); // TODO: check if really logged in final sheets = await apiClient!.fetchSheets(); log.info("${sheets.length} sheets fetched"); final sheetsSorted = await sortSheetsByRecency(sheets); log.info("${sheetsSorted.length} sheets sorted"); // TODO: make work // final changeQueue = await _storageHelper.readChangeQueue(); // changeQueue.applyToSheets(sheetsSorted); // log.info("${changeQueue.length()} changes applied"); return sheetsSorted; } Future> sortSheetsByRecency(List sheets) async { final accessTimes = await _storageHelper.readSheetAccessTimes(); sheets.sort((a, b) { var dateA = accessTimes[a.uuid]; var dateB = accessTimes[b.uuid]; if (dateA == null || a.updatedAt.isAfter(dateA)) { dateA = a.updatedAt; } if (dateB == null || b.updatedAt.isAfter(dateB)) { dateB = b.updatedAt; } return dateB.compareTo(dateA); }); return sheets; } Future _logOut() async { // Delete saved jwt await _storageHelper.writeSecure(SecureStorageKey.jwt, null); if (!mounted) return; // Widget already removed Navigator.of( context, ).pushReplacement(MaterialPageRoute(builder: (_) => LoginPage())); } void switchShufflingState(bool newState) async { if (newState) { (await sheets).shuffle(); } else { sheets = sortSheetsByRecency(await sheets); } setState(() { shuffling = newState; }); } Drawer _buildDrawer() { return Drawer( child: SafeArea( child: Padding( padding: EdgeInsetsGeometry.directional(start: 10, end: 10, top: 30), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Drawer Actions Column( children: [ ListTile( leading: Icon( Icons.shuffle, color: shuffling ? Colors.blue : null, ), title: const Text('Shuffle'), onTap: () { switchShufflingState(!shuffling); }, ), ListTile( leading: Icon( widget.config.fullscreen ? Icons.fullscreen_exit : Icons.fullscreen, ), title: widget.config.fullscreen ? const Text('Exit Fullscreen') : const Text('Enter Fullscreen'), onTap: toggleFullscreen, ), ListTile( leading: const Icon(Icons.logout), title: const Text('Logout'), onTap: _logOut, ), ], ), // App Info at bottom Padding( padding: const EdgeInsets.all(16.0), child: Text( '$appName v$appVersion', style: const TextStyle(color: Colors.grey), ), ), ], ), ), ), ); } @override void onFullScreenChanged(bool enabled, SystemUiMode? systemUiMode) { setState(() { widget.config.fullscreen = enabled; _storageHelper.writeConfig(widget.config); }); } void toggleFullscreen() { FullScreen.setFullScreen(!widget.config.fullscreen); } @override Widget build(BuildContext context) { return Scaffold( // Icon for drawer appears automatically appBar: AppBar(title: const Text("Sheetless")), endDrawer: _buildDrawer(), body: FutureBuilder( future: sheets, builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.hasData) { return SheetsWidget( sheets: snapshot.data!, onSheetOpenRequest: (sheet) { _storageHelper.writeSheetAccessTime(sheet.uuid, DateTime.now()); Navigator.push( context, MaterialPageRoute( builder: (context) => SheetViewerPage( sheet: sheet, apiClient: apiClient!, config: widget.config, ), ), ); }, ); } else if (snapshot.hasError) { log.warning("Error loading sheets:", snapshot.error); return Center( child: Text( style: Theme.of( context, ).textTheme.displaySmall!.copyWith(color: Colors.red), textAlign: TextAlign.center, snapshot.error.toString(), ), ); } else { return const Center(child: CircularProgressIndicator()); } }, ), ); } }