231 lines
6.6 KiB
Dart
231 lines
6.6 KiB
Dart
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<MyHomePage> createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> with FullScreenListener {
|
|
ApiClient? apiClient;
|
|
Future<bool> apiLoggedIn = Future.value(false);
|
|
final StorageHelper _storageHelper = StorageHelper();
|
|
final log = Logger("MyHomePage");
|
|
String? appName;
|
|
String? appVersion;
|
|
bool shuffling = false;
|
|
late Future<List<Sheet>> 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<void> _loadAppInfo() async {
|
|
final info = await PackageInfo.fromPlatform();
|
|
setState(() {
|
|
appName = info.appName;
|
|
appVersion = info.version;
|
|
});
|
|
}
|
|
|
|
Future<List<Sheet>> 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<List<Sheet>> sortSheetsByRecency(List<Sheet> 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<void> _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<List<Sheet>> 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());
|
|
}
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|