|
|
|
@ -14,17 +14,25 @@ class DeckStorage {
|
|
|
|
static const String _deckIdsKey = 'deck_ids';
|
|
|
|
static const String _deckIdsKey = 'deck_ids';
|
|
|
|
bool _initialized = false;
|
|
|
|
bool _initialized = false;
|
|
|
|
SharedPreferences? _prefs;
|
|
|
|
SharedPreferences? _prefs;
|
|
|
|
final Future<void> _initFuture;
|
|
|
|
Future<void>? _initFuture;
|
|
|
|
|
|
|
|
|
|
|
|
DeckStorage._internal() : _initFuture = SharedPreferences.getInstance().then((prefs) {
|
|
|
|
DeckStorage._internal();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Lazy initialization of SharedPreferences
|
|
|
|
|
|
|
|
Future<void> _initSharedPreferences() async {
|
|
|
|
|
|
|
|
if (_initFuture == null) {
|
|
|
|
|
|
|
|
_initFuture = SharedPreferences.getInstance().then((prefs) {
|
|
|
|
_instance._prefs = prefs;
|
|
|
|
_instance._prefs = prefs;
|
|
|
|
_instance._initialized = true;
|
|
|
|
_instance._initialized = true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
await _initFuture;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Initialize storage with default deck if empty
|
|
|
|
/// Initialize storage with default deck if empty
|
|
|
|
Future<void> initialize() async {
|
|
|
|
Future<void> initialize() async {
|
|
|
|
if (_initialized) return;
|
|
|
|
if (_initialized) return;
|
|
|
|
await _initFuture;
|
|
|
|
await _initSharedPreferences();
|
|
|
|
|
|
|
|
|
|
|
|
// Load existing decks
|
|
|
|
// Load existing decks
|
|
|
|
final deckIds = _prefs!.getStringList(_deckIdsKey) ?? [];
|
|
|
|
final deckIds = _prefs!.getStringList(_deckIdsKey) ?? [];
|
|
|
|
@ -39,7 +47,7 @@ class DeckStorage {
|
|
|
|
/// Ensure storage is initialized
|
|
|
|
/// Ensure storage is initialized
|
|
|
|
Future<void> _ensureInitialized() async {
|
|
|
|
Future<void> _ensureInitialized() async {
|
|
|
|
if (!_initialized) {
|
|
|
|
if (!_initialized) {
|
|
|
|
await _initFuture;
|
|
|
|
await _initSharedPreferences();
|
|
|
|
await initialize();
|
|
|
|
await initialize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -75,10 +83,10 @@ class DeckStorage {
|
|
|
|
'attemptHistory': deck.attemptHistory.map((entry) => {
|
|
|
|
'attemptHistory': deck.attemptHistory.map((entry) => {
|
|
|
|
'timestamp': entry.timestamp,
|
|
|
|
'timestamp': entry.timestamp,
|
|
|
|
'totalQuestions': entry.totalQuestions,
|
|
|
|
'totalQuestions': entry.totalQuestions,
|
|
|
|
'correctAnswers': entry.correctAnswers,
|
|
|
|
'correctCount': entry.correctCount,
|
|
|
|
'incorrectAnswers': entry.incorrectAnswers,
|
|
|
|
'percentageCorrect': entry.percentageCorrect,
|
|
|
|
'skippedAnswers': entry.skippedAnswers,
|
|
|
|
'timeSpent': entry.timeSpent,
|
|
|
|
'timeSpentSeconds': entry.timeSpentSeconds,
|
|
|
|
'unknownPercentage': entry.unknownPercentage,
|
|
|
|
}).toList(),
|
|
|
|
}).toList(),
|
|
|
|
'incompleteAttempt': deck.incompleteAttempt != null ? {
|
|
|
|
'incompleteAttempt': deck.incompleteAttempt != null ? {
|
|
|
|
'attemptId': deck.incompleteAttempt!.attemptId,
|
|
|
|
'attemptId': deck.incompleteAttempt!.attemptId,
|
|
|
|
@ -145,13 +153,23 @@ class DeckStorage {
|
|
|
|
final historyJson = json['attemptHistory'] as List<dynamic>? ?? [];
|
|
|
|
final historyJson = json['attemptHistory'] as List<dynamic>? ?? [];
|
|
|
|
final attemptHistory = historyJson.map((entryJson) {
|
|
|
|
final attemptHistory = historyJson.map((entryJson) {
|
|
|
|
final entryMap = entryJson as Map<String, dynamic>;
|
|
|
|
final entryMap = entryJson as Map<String, dynamic>;
|
|
|
|
|
|
|
|
// Support both old and new field names for backward compatibility
|
|
|
|
|
|
|
|
final correctCount = entryMap['correctCount'] as int? ??
|
|
|
|
|
|
|
|
entryMap['correctAnswers'] as int? ?? 0;
|
|
|
|
|
|
|
|
final totalQuestions = entryMap['totalQuestions'] as int? ?? 0;
|
|
|
|
|
|
|
|
final timeSpent = entryMap['timeSpent'] as int? ??
|
|
|
|
|
|
|
|
(entryMap['timeSpentSeconds'] as int? ?? 0) * 1000; // Convert seconds to milliseconds
|
|
|
|
|
|
|
|
// Calculate percentageCorrect if not present
|
|
|
|
|
|
|
|
final percentageCorrect = entryMap['percentageCorrect'] as double? ??
|
|
|
|
|
|
|
|
(totalQuestions > 0 ? (correctCount / totalQuestions * 100) : 0.0);
|
|
|
|
|
|
|
|
final unknownPercentage = entryMap['unknownPercentage'] as double? ?? 0.0;
|
|
|
|
return AttemptHistoryEntry(
|
|
|
|
return AttemptHistoryEntry(
|
|
|
|
timestamp: entryMap['timestamp'] as int? ?? 0,
|
|
|
|
timestamp: entryMap['timestamp'] as int? ?? 0,
|
|
|
|
totalQuestions: entryMap['totalQuestions'] as int? ?? 0,
|
|
|
|
totalQuestions: totalQuestions,
|
|
|
|
correctAnswers: entryMap['correctAnswers'] as int? ?? 0,
|
|
|
|
correctCount: correctCount,
|
|
|
|
incorrectAnswers: entryMap['incorrectAnswers'] as int? ?? 0,
|
|
|
|
percentageCorrect: percentageCorrect,
|
|
|
|
skippedAnswers: entryMap['skippedAnswers'] as int? ?? 0,
|
|
|
|
timeSpent: timeSpent,
|
|
|
|
timeSpentSeconds: entryMap['timeSpentSeconds'] as int?,
|
|
|
|
unknownPercentage: unknownPercentage,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}).toList();
|
|
|
|
}).toList();
|
|
|
|
|
|
|
|
|
|
|
|
|