score_manager.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import 'package:shared_preferences/shared_preferences.dart';
  2. import 'dart:convert';
  3. class ScoreManager {
  4. static const String _todayScoreKey = 'today_score';
  5. static const String _totalScoreKey = 'total_score';
  6. static const String _dailyHistoryKey = 'daily_history';
  7. static const String _lastDateKey = 'last_date';
  8. static int _todayScore = 0;
  9. static int _totalScore = 0;
  10. static Map<String, int> _dailyHistory = {};
  11. static int get todayScore => _todayScore;
  12. static int get totalScore => _totalScore;
  13. static Map<String, int> get dailyHistory => Map.from(_dailyHistory);
  14. /// Initialize the score manager - loads saved data
  15. static Future<void> initialize() async {
  16. await _loadScores();
  17. await _checkNewDay();
  18. }
  19. /// Add points to today's score
  20. static Future<void> addScore(int points) async {
  21. _todayScore += points;
  22. _totalScore += points;
  23. await _saveScores();
  24. }
  25. /// Reset today's score (for testing purposes)
  26. static Future<void> resetTodayScore() async {
  27. _todayScore = 0;
  28. await _saveScores();
  29. }
  30. /// Get scores for the last N days (for charts)
  31. static List<MapEntry<String, int>> getLastDaysScores(int days) {
  32. final now = DateTime.now();
  33. final result = <MapEntry<String, int>>[];
  34. for (int i = days - 1; i >= 0; i--) {
  35. final date = now.subtract(Duration(days: i));
  36. final dateStr = _formatDate(date);
  37. final score = _dailyHistory[dateStr] ?? 0;
  38. result.add(MapEntry(dateStr, score));
  39. }
  40. return result;
  41. }
  42. /// Get weekly totals for the last N weeks
  43. static List<MapEntry<String, int>> getWeeklyScores(int weeks) {
  44. final now = DateTime.now();
  45. final result = <MapEntry<String, int>>[];
  46. for (int i = weeks - 1; i >= 0; i--) {
  47. final weekStart = now.subtract(Duration(days: now.weekday - 1 + (i * 7)));
  48. int weekTotal = 0;
  49. for (int day = 0; day < 7; day++) {
  50. final date = weekStart.add(Duration(days: day));
  51. final dateStr = _formatDate(date);
  52. weekTotal += _dailyHistory[dateStr] ?? 0;
  53. }
  54. final weekLabel = 'Week of ${_formatDate(weekStart)}';
  55. result.add(MapEntry(weekLabel, weekTotal));
  56. }
  57. return result;
  58. }
  59. /// Get total bubbles popped (approximate based on score)
  60. static int get totalBubblesPopped => _totalScore ~/ 10; // 10 points per bubble
  61. /// Get average daily score
  62. static double get averageDailyScore {
  63. if (_dailyHistory.isEmpty) return 0.0;
  64. final total = _dailyHistory.values.fold(0, (a, b) => a + b);
  65. return total / _dailyHistory.length;
  66. }
  67. /// Get current streak (consecutive days with score > 0)
  68. static int get currentStreak {
  69. final now = DateTime.now();
  70. int streak = 0;
  71. for (int i = 0; i < 365; i++) { // Check up to a year
  72. final date = now.subtract(Duration(days: i));
  73. final dateStr = _formatDate(date);
  74. final score = _dailyHistory[dateStr] ?? 0;
  75. if (score > 0) {
  76. streak++;
  77. } else {
  78. break;
  79. }
  80. }
  81. return streak;
  82. }
  83. /// Get best day score
  84. static int get bestDayScore {
  85. if (_dailyHistory.isEmpty) return _todayScore;
  86. return _dailyHistory.values.fold(_todayScore, (a, b) => a > b ? a : b);
  87. }
  88. static Future<void> _loadScores() async {
  89. final prefs = await SharedPreferences.getInstance();
  90. // Handle potential type mismatches from previous versions
  91. try {
  92. _todayScore = prefs.getInt(_todayScoreKey) ?? 0;
  93. } catch (e) {
  94. // Clean up corrupted data
  95. await prefs.remove(_todayScoreKey);
  96. _todayScore = 0;
  97. }
  98. try {
  99. _totalScore = prefs.getInt(_totalScoreKey) ?? 0;
  100. } catch (e) {
  101. // Clean up corrupted data
  102. await prefs.remove(_totalScoreKey);
  103. _totalScore = 0;
  104. }
  105. final historyJson = prefs.getString(_dailyHistoryKey) ?? '{}';
  106. try {
  107. final historyMap = json.decode(historyJson) as Map<String, dynamic>;
  108. _dailyHistory = historyMap.map((key, value) => MapEntry(key, value as int));
  109. } catch (e) {
  110. _dailyHistory = {};
  111. // Clean up corrupted data
  112. await prefs.remove(_dailyHistoryKey);
  113. }
  114. }
  115. static Future<void> _saveScores() async {
  116. final prefs = await SharedPreferences.getInstance();
  117. await prefs.setInt(_todayScoreKey, _todayScore);
  118. await prefs.setInt(_totalScoreKey, _totalScore);
  119. await prefs.setString(_lastDateKey, DateTime.now().toIso8601String().split('T')[0]);
  120. // Save today's score to history
  121. final today = _formatDate(DateTime.now());
  122. _dailyHistory[today] = _todayScore;
  123. final historyJson = json.encode(_dailyHistory);
  124. await prefs.setString(_dailyHistoryKey, historyJson);
  125. }
  126. static Future<void> _checkNewDay() async {
  127. final prefs = await SharedPreferences.getInstance();
  128. final lastDate = prefs.getString(_lastDateKey);
  129. final today = _formatDate(DateTime.now());
  130. if (lastDate != today) {
  131. // New day - save yesterday's score and reset today's
  132. if (lastDate != null && _todayScore > 0) {
  133. _dailyHistory[lastDate] = _todayScore;
  134. }
  135. _todayScore = 0;
  136. await prefs.setString(_lastDateKey, today);
  137. await _saveScores();
  138. }
  139. }
  140. static String _formatDate(DateTime date) {
  141. return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
  142. }
  143. /// Clean up old data (keep only last 365 days)
  144. static Future<void> cleanupOldData() async {
  145. final cutoffDate = DateTime.now().subtract(const Duration(days: 365));
  146. final cutoffStr = _formatDate(cutoffDate);
  147. _dailyHistory.removeWhere((date, score) => date.compareTo(cutoffStr) < 0);
  148. await _saveScores();
  149. }
  150. }