mock data and persistence
This commit is contained in:
@@ -73,7 +73,7 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
||||
void _done() {
|
||||
if (_deck == null) return;
|
||||
// Save the updated deck to storage
|
||||
_deckStorage.saveDeck(_deck!);
|
||||
_deckStorage.saveDeckSync(_deck!);
|
||||
// Navigate back to deck list
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context,
|
||||
|
||||
@@ -449,7 +449,7 @@ class _AttemptScreenState extends State<AttemptScreen> {
|
||||
|
||||
// Update deck with incomplete attempt
|
||||
final updatedDeck = _deck!.copyWith(incompleteAttempt: incompleteAttempt);
|
||||
_deckStorage.saveDeck(updatedDeck);
|
||||
_deckStorage.saveDeckSync(updatedDeck);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
@@ -485,7 +485,7 @@ class _AttemptScreenState extends State<AttemptScreen> {
|
||||
);
|
||||
|
||||
// Save the updated deck to storage
|
||||
_deckStorage.saveDeck(updatedDeckWithHistory);
|
||||
_deckStorage.saveDeckSync(updatedDeckWithHistory);
|
||||
|
||||
Navigator.pushReplacementNamed(
|
||||
context,
|
||||
|
||||
@@ -225,7 +225,7 @@ class _DeckConfigScreenState extends State<DeckConfigScreen> {
|
||||
if (updatedDeck != null && updatedDeck is Deck && mounted) {
|
||||
// Reload the deck from storage to get the latest state
|
||||
final deckStorage = DeckStorage();
|
||||
final refreshedDeck = deckStorage.getDeck(updatedDeck.id);
|
||||
final refreshedDeck = deckStorage.getDeckSync(updatedDeck.id);
|
||||
if (refreshedDeck != null) {
|
||||
setState(() {
|
||||
_deck = refreshedDeck;
|
||||
@@ -272,7 +272,7 @@ class _DeckConfigScreenState extends State<DeckConfigScreen> {
|
||||
|
||||
// Save the reset deck to storage
|
||||
final deckStorage = DeckStorage();
|
||||
deckStorage.saveDeck(resetDeck);
|
||||
deckStorage.saveDeckSync(resetDeck);
|
||||
|
||||
// Show success message
|
||||
if (mounted) {
|
||||
|
||||
@@ -96,7 +96,7 @@ class _DeckCreateScreenState extends State<DeckCreateScreen> {
|
||||
attemptHistory: [],
|
||||
);
|
||||
|
||||
_deckStorage.saveDeck(newDeck);
|
||||
_deckStorage.saveDeckSync(newDeck);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
|
||||
@@ -118,7 +118,7 @@ class _DeckEditScreenState extends State<DeckEditScreen> {
|
||||
currentAttemptIndex: _deck!.currentAttemptIndex,
|
||||
);
|
||||
|
||||
_deckStorage.saveDeck(updatedDeck);
|
||||
_deckStorage.saveDeckSync(updatedDeck);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
|
||||
@@ -109,7 +109,7 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
|
||||
|
||||
// Save deck to storage
|
||||
final deckStorage = DeckStorage();
|
||||
deckStorage.saveDeck(deck);
|
||||
deckStorage.saveDeckSync(deck);
|
||||
|
||||
// Navigate back to deck list
|
||||
Navigator.pop(context);
|
||||
@@ -121,12 +121,11 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
void _useDefaultDeck() {
|
||||
final defaultDeck = DefaultDeck.deck;
|
||||
void _useDefaultDeck() async {
|
||||
final deckStorage = DeckStorage();
|
||||
|
||||
// Check if default deck already exists
|
||||
if (deckStorage.hasDeck(defaultDeck.id)) {
|
||||
if (deckStorage.hasDeckSync(DefaultDeck.deck.id)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Default deck already exists'),
|
||||
@@ -136,8 +135,24 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Show dialog to ask if user wants mock data
|
||||
final includeMockData = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => _MockDataDialog(),
|
||||
);
|
||||
|
||||
if (includeMockData == null) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the appropriate deck version
|
||||
final defaultDeck = includeMockData
|
||||
? DefaultDeck.deckWithMockData
|
||||
: DefaultDeck.deck;
|
||||
|
||||
// Save default deck to storage
|
||||
deckStorage.saveDeck(defaultDeck);
|
||||
deckStorage.saveDeckSync(defaultDeck);
|
||||
|
||||
// Navigate back to deck list
|
||||
Navigator.pop(context);
|
||||
@@ -420,3 +435,55 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _MockDataDialog extends StatefulWidget {
|
||||
@override
|
||||
State<_MockDataDialog> createState() => _MockDataDialogState();
|
||||
}
|
||||
|
||||
class _MockDataDialogState extends State<_MockDataDialog> {
|
||||
bool _includeMockData = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Add Default Deck'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Would you like to include mock attempt history and progress data?',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CheckboxListTile(
|
||||
value: _includeMockData,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_includeMockData = value ?? false;
|
||||
});
|
||||
},
|
||||
title: const Text('Include mock data'),
|
||||
subtitle: const Text(
|
||||
'Adds 4 sample attempts showing progress over time',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, null),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, _includeMockData),
|
||||
child: const Text('Add Deck'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,17 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
}
|
||||
|
||||
void _loadDecks() {
|
||||
// Use sync version for immediate UI update, will work once storage is initialized
|
||||
setState(() {
|
||||
_decks = _deckStorage.getAllDecks();
|
||||
_decks = _deckStorage.getAllDecksSync();
|
||||
});
|
||||
// Also trigger async load to ensure we have latest data
|
||||
_deckStorage.getAllDecks().then((decks) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_decks = decks;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,7 +70,7 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
_deckStorage.deleteDeck(deck.id);
|
||||
_deckStorage.deleteDeckSync(deck.id);
|
||||
_loadDecks();
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -112,7 +121,7 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
_deckStorage.saveDeck(clonedDeck);
|
||||
_deckStorage.saveDeckSync(clonedDeck);
|
||||
_loadDecks();
|
||||
|
||||
if (mounted) {
|
||||
@@ -181,16 +190,36 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _useDefaultDeck() {
|
||||
final defaultDeck = DefaultDeck.deck;
|
||||
|
||||
void _useDefaultDeck() async {
|
||||
// Check if default deck already exists
|
||||
if (_deckStorage.hasDeck(defaultDeck.id)) {
|
||||
final baseDeck = DefaultDeck.deck;
|
||||
final deckExists = _deckStorage.hasDeckSync(baseDeck.id);
|
||||
|
||||
// Show dialog to ask if user wants mock data (only if deck doesn't exist)
|
||||
bool? includeMockData = false;
|
||||
if (!deckExists) {
|
||||
includeMockData = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => _MockDataDialog(),
|
||||
);
|
||||
|
||||
if (includeMockData == null) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the appropriate deck version
|
||||
final defaultDeck = includeMockData
|
||||
? DefaultDeck.deckWithMockData
|
||||
: DefaultDeck.deck;
|
||||
|
||||
if (deckExists) {
|
||||
// If it exists, create a copy with a new ID
|
||||
final clonedDeck = defaultDeck.copyWith(
|
||||
id: '${defaultDeck.id}_${DateTime.now().millisecondsSinceEpoch}',
|
||||
);
|
||||
_deckStorage.saveDeck(clonedDeck);
|
||||
_deckStorage.saveDeckSync(clonedDeck);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -202,7 +231,7 @@ class _DeckListScreenState extends State<DeckListScreen> {
|
||||
}
|
||||
} else {
|
||||
// Save default deck to storage
|
||||
_deckStorage.saveDeck(defaultDeck);
|
||||
_deckStorage.saveDeckSync(defaultDeck);
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -421,3 +450,55 @@ class _StatChip extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _MockDataDialog extends StatefulWidget {
|
||||
@override
|
||||
State<_MockDataDialog> createState() => _MockDataDialogState();
|
||||
}
|
||||
|
||||
class _MockDataDialogState extends State<_MockDataDialog> {
|
||||
bool _includeMockData = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Add Default Deck'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Would you like to include mock attempt history and progress data?',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
CheckboxListTile(
|
||||
value: _includeMockData,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_includeMockData = value ?? false;
|
||||
});
|
||||
},
|
||||
title: const Text('Include mock data'),
|
||||
subtitle: const Text(
|
||||
'Adds 4 sample attempts showing progress over time',
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, null),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, _includeMockData),
|
||||
child: const Text('Add Deck'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
// ALWAYS load the latest version from storage to ensure we have the most up-to-date deck
|
||||
// This is critical for getting the latest incomplete attempt state
|
||||
// Don't rely on route arguments - they might be stale
|
||||
final storedDeck = _deckStorage.getDeck(args.id);
|
||||
final storedDeck = _deckStorage.getDeckSync(args.id);
|
||||
final deckToUse = storedDeck ?? args;
|
||||
|
||||
// Always update to get the latest state from storage
|
||||
@@ -37,7 +37,7 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
|
||||
void _saveDeck() {
|
||||
if (_deck != null) {
|
||||
_deckStorage.saveDeck(_deck!);
|
||||
_deckStorage.saveDeckSync(_deck!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
// Force reload from storage before checking for incomplete attempts to ensure we have latest state
|
||||
Deck? deckToCheck = _deck;
|
||||
if (_deck != null) {
|
||||
final freshDeck = _deckStorage.getDeck(_deck!.id);
|
||||
final freshDeck = _deckStorage.getDeckSync(_deck!.id);
|
||||
if (freshDeck != null) {
|
||||
setState(() {
|
||||
_deck = freshDeck;
|
||||
@@ -117,8 +117,8 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
final updatedDeck = deckToCheck.copyWith(clearIncompleteAttempt: true);
|
||||
|
||||
// Save to storage multiple times to ensure it's persisted
|
||||
_deckStorage.saveDeck(updatedDeck);
|
||||
_deckStorage.saveDeck(updatedDeck); // Save twice to be sure
|
||||
_deckStorage.saveDeckSync(updatedDeck);
|
||||
_deckStorage.saveDeckSync(updatedDeck); // Save twice to be sure
|
||||
|
||||
// Update local state immediately with cleared deck
|
||||
setState(() {
|
||||
@@ -128,13 +128,13 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
// Force reload from storage multiple times to verify it's cleared
|
||||
Deck? finalClearedDeck = updatedDeck;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final verifiedDeck = _deckStorage.getDeck(updatedDeck.id);
|
||||
final verifiedDeck = _deckStorage.getDeckSync(updatedDeck.id);
|
||||
|
||||
if (verifiedDeck != null) {
|
||||
// Ensure incomplete attempt is null even if storage had it
|
||||
if (verifiedDeck.incompleteAttempt != null) {
|
||||
final clearedDeck = verifiedDeck.copyWith(clearIncompleteAttempt: true);
|
||||
_deckStorage.saveDeck(clearedDeck);
|
||||
_deckStorage.saveDeckSync(clearedDeck);
|
||||
setState(() {
|
||||
_deck = clearedDeck;
|
||||
});
|
||||
@@ -211,7 +211,7 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
// Also ensure storage has the cleared state
|
||||
final deckToUse = deckToCheck.copyWith(clearIncompleteAttempt: true);
|
||||
// Ensure storage also has the cleared state
|
||||
_deckStorage.saveDeck(deckToUse);
|
||||
_deckStorage.saveDeckSync(deckToUse);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.attempt,
|
||||
@@ -226,7 +226,7 @@ class _DeckOverviewScreenState extends State<DeckOverviewScreen> {
|
||||
void _refreshDeck() {
|
||||
if (_deck != null) {
|
||||
// Reload the deck from storage to get the latest state (including incomplete attempts)
|
||||
final refreshedDeck = _deckStorage.getDeck(_deck!.id);
|
||||
final refreshedDeck = _deckStorage.getDeckSync(_deck!.id);
|
||||
if (refreshedDeck != null && mounted) {
|
||||
setState(() {
|
||||
_deck = refreshedDeck;
|
||||
|
||||
Reference in New Issue
Block a user