Complete refactor to clean up project

This commit is contained in:
2026-02-04 11:36:02 +01:00
parent 4f380b5444
commit 704bd0b928
29 changed files with 2057 additions and 1464 deletions

View File

@@ -0,0 +1,96 @@
import 'dart:collection';
import 'sheet.dart';
/// Types of changes that can be queued for server synchronization.
enum ChangeType {
sheetNameChange,
composerNameChange,
addTagChange,
removeTagChange,
}
/// Represents a single pending change to be synced with the server.
///
/// Changes are stored locally when offline and applied once
/// the device regains connectivity.
class Change {
final ChangeType type;
final String sheetUuid;
final String value;
Change({
required this.type,
required this.sheetUuid,
required this.value,
});
/// Serializes this change to a map for storage.
Map<String, dynamic> toMap() => {
'type': type.index,
'sheetUuid': sheetUuid,
'value': value,
};
/// Deserializes a change from a stored map.
///
/// Note: Adding new [ChangeType] values may cause issues with
/// previously stored changes that use index-based serialization.
factory Change.fromMap(Map<dynamic, dynamic> map) {
return Change(
type: ChangeType.values[map['type']],
sheetUuid: map['sheetUuid'],
value: map['value'],
);
}
}
/// A queue of pending changes to be synchronized with the server.
///
/// Changes are stored in FIFO order (oldest first) and applied
/// to sheets in sequence when syncing.
class ChangeQueue {
final Queue<Change> _queue = Queue();
ChangeQueue();
/// Adds a change to the end of the queue.
void addChange(Change change) {
_queue.addLast(change);
}
/// Returns the number of pending changes.
int get length => _queue.length;
/// Whether the queue has any pending changes.
bool get isEmpty => _queue.isEmpty;
/// Whether the queue has pending changes.
bool get isNotEmpty => _queue.isNotEmpty;
/// Applies all queued changes to the provided list of sheets.
///
/// Each change modifies the corresponding sheet's properties
/// based on the change type.
void applyToSheets(List<Sheet> sheets) {
for (final change in _queue) {
final sheet = sheets.firstWhere(
(s) => s.uuid == change.sheetUuid,
orElse: () => throw StateError(
'Sheet with UUID ${change.sheetUuid} not found',
),
);
switch (change.type) {
case ChangeType.sheetNameChange:
sheet.name = change.value;
case ChangeType.composerNameChange:
sheet.composerName = change.value;
case ChangeType.addTagChange:
throw UnimplementedError('Tag support not yet implemented');
case ChangeType.removeTagChange:
throw UnimplementedError('Tag support not yet implemented');
}
}
}
}

View File

@@ -0,0 +1,37 @@
/// Application configuration model.
///
/// Stores user preferences that are persisted across sessions,
/// such as display mode and fullscreen settings.
class Config {
/// Storage keys for persistence
static const String keyTwoPageMode = 'twoPageMode';
static const String keyFullscreen = 'fullscreen';
/// Whether to display two pages side-by-side (for tablets/landscape).
bool twoPageMode;
/// Whether the app is in fullscreen mode.
bool fullscreen;
Config({
required this.twoPageMode,
required this.fullscreen,
});
/// Creates a default configuration with all options disabled.
factory Config.defaultConfig() => Config(
twoPageMode: false,
fullscreen: false,
);
/// Creates a copy of this config with optional overrides.
Config copyWith({
bool? twoPageMode,
bool? fullscreen,
}) {
return Config(
twoPageMode: twoPageMode ?? this.twoPageMode,
fullscreen: fullscreen ?? this.fullscreen,
);
}
}

View File

@@ -0,0 +1,40 @@
/// Data model representing a sheet music document.
///
/// A [Sheet] contains metadata about a piece of sheet music,
/// including its title, composer information, and timestamps.
class Sheet {
final String uuid;
String name;
String composerUuid;
String composerName;
DateTime updatedAt;
Sheet({
required this.uuid,
required this.name,
required this.composerUuid,
required this.composerName,
required this.updatedAt,
});
/// Creates a [Sheet] from a JSON map returned by the API.
factory Sheet.fromJson(Map<String, dynamic> json) {
final composer = json['composer'] as Map<String, dynamic>?;
return Sheet(
uuid: json['uuid'].toString(),
name: json['title'],
composerUuid: json['composer_uuid']?.toString() ?? '',
composerName: composer?['name'] ?? 'Unknown',
updatedAt: DateTime.parse(json['updated_at']),
);
}
/// Converts this sheet to a JSON map for API requests.
Map<String, dynamic> toJson() => {
'uuid': uuid,
'title': name,
'composer_uuid': composerUuid,
'composer_name': composerName,
'updated_at': updatedAt.toIso8601String(),
};
}