Add functionality to edit sheets with save and restore
This commit is contained in:
70
lib/edit_bottom_sheet.dart
Normal file
70
lib/edit_bottom_sheet.dart
Normal file
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sheetless/sheet.dart';
|
||||
|
||||
typedef SheetEditedCallback = void Function(String newName, String newComposer);
|
||||
|
||||
class EditItemBottomSheet extends StatefulWidget {
|
||||
final Sheet sheet;
|
||||
final SheetEditedCallback onSheetEdited;
|
||||
|
||||
const EditItemBottomSheet({
|
||||
super.key,
|
||||
required this.sheet,
|
||||
required this.onSheetEdited,
|
||||
});
|
||||
|
||||
@override
|
||||
State<EditItemBottomSheet> createState() => _EditItemBottomSheetState();
|
||||
}
|
||||
|
||||
class _EditItemBottomSheetState extends State<EditItemBottomSheet> {
|
||||
late TextEditingController sheetNameController;
|
||||
late TextEditingController composerNameController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
sheetNameController = TextEditingController(text: widget.sheet.name);
|
||||
composerNameController = TextEditingController(
|
||||
text: widget.sheet.composerName,
|
||||
);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
MediaQuery.of(context).viewInsets.bottom + 16,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: sheetNameController,
|
||||
decoration: InputDecoration(labelText: "Sheet"),
|
||||
),
|
||||
TextField(
|
||||
controller: composerNameController,
|
||||
decoration: InputDecoration(labelText: "Composer"),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
// TODO: check text fields are not empty
|
||||
// TODO: save on pressing enter
|
||||
widget.onSheetEdited(
|
||||
sheetNameController.text,
|
||||
composerNameController.text,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text("Save"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,11 @@ class _MyHomePageState extends State<MyHomePage> with FullScreenListener {
|
||||
log.info("${sheets.length} sheets fetched");
|
||||
final sheetsSorted = await sortSheetsByAccessTime(sheets);
|
||||
log.info("${sheetsSorted.length} sheets sorted");
|
||||
|
||||
final changeQueue = await _storageHelper.readChangeQueue();
|
||||
changeQueue.applyToSheets(sheetsSorted);
|
||||
log.info("${changeQueue.length()} changes applied");
|
||||
|
||||
return sheetsSorted;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@ import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sheetless/edit_bottom_sheet.dart';
|
||||
import 'package:sheetless/storage_helper.dart';
|
||||
import 'package:sheetless/upload_queue.dart';
|
||||
|
||||
class Sheet {
|
||||
final String uuid;
|
||||
final String name;
|
||||
final String composerUuid;
|
||||
final String composerName;
|
||||
String name;
|
||||
String composerUuid;
|
||||
String composerName;
|
||||
|
||||
Sheet({
|
||||
required this.uuid,
|
||||
@@ -92,6 +94,40 @@ class _SheetsWidgetState extends State<SheetsWidget> {
|
||||
_searchController.clear();
|
||||
}
|
||||
|
||||
void _openEditSheet(BuildContext context, Sheet sheet) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (_) => EditItemBottomSheet(
|
||||
sheet: sheet,
|
||||
onSheetEdited: (String newName, String newComposer) {
|
||||
if (newName != sheet.name) {
|
||||
storageHelper.writeChange(
|
||||
Change(
|
||||
type: ChangeType.sheetNameChange,
|
||||
sheetUuid: sheet.uuid,
|
||||
value: newName,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (newComposer != sheet.composerName) {
|
||||
storageHelper.writeChange(
|
||||
Change(
|
||||
type: ChangeType.composerNameChange,
|
||||
sheetUuid: sheet.uuid,
|
||||
value: newComposer,
|
||||
),
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
sheet.name = newName;
|
||||
sheet.composerName = newComposer;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@@ -134,6 +170,7 @@ class _SheetsWidgetState extends State<SheetsWidget> {
|
||||
widget.sheets.remove(sheet);
|
||||
widget.sheets.insert(0, sheet);
|
||||
}),
|
||||
onLongPress: () => _openEditSheet(context, sheet),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:sheetless/upload_queue.dart';
|
||||
|
||||
enum SecureStorageKey { url, jwt, email }
|
||||
|
||||
@@ -18,6 +19,7 @@ class Config {
|
||||
class StorageHelper {
|
||||
final sheetAccessTimesBox = "sheetAccessTimes";
|
||||
final configBox = "config";
|
||||
final changeQueueBox = "changeQueue";
|
||||
|
||||
late FlutterSecureStorage secureStorage;
|
||||
|
||||
@@ -60,4 +62,23 @@ class StorageHelper {
|
||||
final box = await Hive.openBox(sheetAccessTimesBox);
|
||||
await box.put(uuid, datetime.toIso8601String());
|
||||
}
|
||||
|
||||
Future<void> writeChange(Change change) async {
|
||||
final box = await Hive.openBox(changeQueueBox);
|
||||
box.add(change.toMap());
|
||||
}
|
||||
|
||||
Future<ChangeQueue> readChangeQueue() async {
|
||||
final box = await Hive.openBox(changeQueueBox);
|
||||
ChangeQueue queue = ChangeQueue();
|
||||
for (Map<dynamic, dynamic> map in box.values) {
|
||||
queue.addChange(Change.fromMap(map));
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
Future<void> deleteOldestChange(Change change) async {
|
||||
final box = await Hive.openBox(changeQueueBox);
|
||||
box.deleteAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
66
lib/upload_queue.dart
Normal file
66
lib/upload_queue.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:sheetless/sheet.dart';
|
||||
|
||||
enum ChangeType {
|
||||
sheetNameChange,
|
||||
composerNameChange,
|
||||
addTagChange,
|
||||
removeTagChange,
|
||||
}
|
||||
|
||||
class Change {
|
||||
final ChangeType type;
|
||||
final String sheetUuid;
|
||||
final String value;
|
||||
|
||||
Change({required this.type, required this.sheetUuid, required this.value});
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
"type": type.index,
|
||||
"sheetUuid": sheetUuid,
|
||||
"value": value,
|
||||
};
|
||||
|
||||
factory Change.fromMap(Map<dynamic, dynamic> map) {
|
||||
return Change(
|
||||
type: ChangeType
|
||||
.values[map["type"]], // TODO: this will create problems if new enums are added
|
||||
sheetUuid: map["sheetUuid"],
|
||||
value: map["value"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChangeQueue {
|
||||
// Queue with oldest change first
|
||||
final Queue<Change> queue = Queue();
|
||||
|
||||
ChangeQueue() {}
|
||||
|
||||
void addChange(Change change) {
|
||||
queue.addLast(change);
|
||||
}
|
||||
|
||||
int length() {
|
||||
return queue.length;
|
||||
}
|
||||
|
||||
void applyToSheets(List<Sheet> sheets) {
|
||||
for (Change change in queue) {
|
||||
var sheet = sheets.where((sheet) => sheet.uuid == change.sheetUuid).first;
|
||||
switch (change.type) {
|
||||
case ChangeType.sheetNameChange:
|
||||
sheet.name = change.value;
|
||||
case ChangeType.composerNameChange:
|
||||
sheet.composerName = change.value;
|
||||
case ChangeType.addTagChange:
|
||||
// TODO: Handle this case.
|
||||
throw UnimplementedError();
|
||||
case ChangeType.removeTagChange:
|
||||
// TODO: Handle this case.
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user