Files
FocusBuddy/lib/services/achievement_service.dart
2025-11-27 13:37:10 +08:00

112 lines
3.5 KiB
Dart

import '../models/user_progress.dart';
import '../models/achievement_config.dart';
/// Service for managing achievements
class AchievementService {
/// Check for newly unlocked achievements asynchronously
/// Returns list of newly unlocked achievement IDs
Future<List<String>> checkAchievementsAsync(UserProgress progress) async {
List<String> newlyUnlocked = [];
for (var achievement in AchievementConfig.all) {
// Skip if already unlocked
if (progress.unlockedAchievements.containsKey(achievement.id)) {
continue;
}
// Check if requirement is met
bool unlocked = false;
switch (achievement.type) {
case AchievementType.sessionCount:
unlocked = progress.totalSessions >= achievement.requiredValue;
break;
case AchievementType.distractionCount:
unlocked = progress.totalDistractions >= achievement.requiredValue;
break;
case AchievementType.totalMinutes:
unlocked = progress.totalFocusMinutes >= achievement.requiredValue;
break;
case AchievementType.consecutiveDays:
unlocked = progress.consecutiveCheckIns >= achievement.requiredValue;
break;
}
if (unlocked) {
// Mark as unlocked with timestamp
progress.unlockedAchievements[achievement.id] = DateTime.now();
// Award bonus points
progress.totalPoints += achievement.bonusPoints;
progress.currentPoints += achievement.bonusPoints;
newlyUnlocked.add(achievement.id);
}
}
return newlyUnlocked;
}
/// Get progress towards a specific achievement (0.0 - 1.0)
double getAchievementProgress(
UserProgress progress,
AchievementConfig achievement,
) {
int currentValue = 0;
switch (achievement.type) {
case AchievementType.sessionCount:
currentValue = progress.totalSessions;
break;
case AchievementType.distractionCount:
currentValue = progress.totalDistractions;
break;
case AchievementType.totalMinutes:
currentValue = progress.totalFocusMinutes;
break;
case AchievementType.consecutiveDays:
currentValue = progress.consecutiveCheckIns;
break;
}
return (currentValue / achievement.requiredValue).clamp(0.0, 1.0);
}
/// Get current value for achievement type
int getAchievementCurrentValue(
UserProgress progress,
AchievementConfig achievement,
) {
switch (achievement.type) {
case AchievementType.sessionCount:
return progress.totalSessions;
case AchievementType.distractionCount:
return progress.totalDistractions;
case AchievementType.totalMinutes:
return progress.totalFocusMinutes;
case AchievementType.consecutiveDays:
return progress.consecutiveCheckIns;
}
}
/// Get all achievements with their current progress
Map<AchievementConfig, double> getAllAchievementsWithProgress(
UserProgress progress,
) {
final result = <AchievementConfig, double>{};
for (var achievement in AchievementConfig.all) {
result[achievement] = getAchievementProgress(progress, achievement);
}
return result;
}
/// Get newly unlocked achievements since last check
/// This can be used to show notifications for achievements unlocked in background
List<String> getNewlyUnlockedAchievements(
UserProgress progress,
Set<String> previouslyUnlocked,
) {
final currentlyUnlocked = progress.unlockedAchievements.keys.toSet();
return currentlyUnlocked.difference(previouslyUnlocked).toList();
}
}