diff --git a/lib/features/gamification/providers/gamification_provider.dart b/lib/features/gamification/providers/gamification_provider.dart index e0ed173..a5498aa 100644 --- a/lib/features/gamification/providers/gamification_provider.dart +++ b/lib/features/gamification/providers/gamification_provider.dart @@ -22,6 +22,7 @@ class GamificationProvider extends ChangeNotifier { String leaderboardTimePeriod = 'all_time'; // 'all_time' | 'this_month' bool isLoading = false; + bool isLeaderboardLoading = false; String? error; // TTL guard — prevents redundant API calls from multiple screens @@ -44,7 +45,6 @@ class GamificationProvider extends ChangeNotifier { try { final results = await Future.wait([ _service.getDashboard(), - _service.getLeaderboard(district: leaderboardDistrict, timePeriod: leaderboardTimePeriod), _service.getShopItems(), _service.getAchievements(), ]); @@ -53,17 +53,12 @@ class GamificationProvider extends ChangeNotifier { profile = dashboard.profile; submissions = dashboard.submissions; - final lbResponse = results[1] as LeaderboardResponse; - leaderboard = lbResponse.entries; - currentUserStats = lbResponse.currentUser; - totalParticipants = lbResponse.totalParticipants; - - shopItems = results[2] as List; + shopItems = results[1] as List; // Prefer achievements from dashboard API; fall back to fetched or existing defaults final dashAchievements = dashboard.achievements; - final fetchedAchievements = results[3] as List; - + final fetchedAchievements = results[2] as List; + if (dashAchievements.isNotEmpty) { achievements = dashAchievements; } else if (fetchedAchievements.isNotEmpty) { @@ -80,12 +75,35 @@ class GamificationProvider extends ChangeNotifier { } } + // --------------------------------------------------------------------------- + // Load leaderboard independently (decoupled from loadAll) + // --------------------------------------------------------------------------- + Future loadLeaderboard() async { + isLeaderboardLoading = true; + notifyListeners(); + try { + final response = await _service.getLeaderboard( + district: leaderboardDistrict, + timePeriod: leaderboardTimePeriod, + ); + leaderboard = response.entries; + currentUserStats = response.currentUser; + totalParticipants = response.totalParticipants; + } catch (e) { + error = userFriendlyError(e); + } finally { + isLeaderboardLoading = false; + notifyListeners(); + } + } + // --------------------------------------------------------------------------- // Change district filter // --------------------------------------------------------------------------- Future setDistrict(String district) async { if (leaderboardDistrict == district) return; leaderboardDistrict = district; + isLeaderboardLoading = true; notifyListeners(); try { final response = await _service.getLeaderboard(district: district, timePeriod: leaderboardTimePeriod); @@ -94,6 +112,8 @@ class GamificationProvider extends ChangeNotifier { totalParticipants = response.totalParticipants; } catch (e) { error = userFriendlyError(e); + } finally { + isLeaderboardLoading = false; } notifyListeners(); } @@ -104,6 +124,7 @@ class GamificationProvider extends ChangeNotifier { Future setTimePeriod(String period) async { if (leaderboardTimePeriod == period) return; leaderboardTimePeriod = period; + isLeaderboardLoading = true; notifyListeners(); try { final response = await _service.getLeaderboard(district: leaderboardDistrict, timePeriod: period); @@ -112,6 +133,8 @@ class GamificationProvider extends ChangeNotifier { totalParticipants = response.totalParticipants; } catch (e) { error = userFriendlyError(e); + } finally { + isLeaderboardLoading = false; } notifyListeners(); } diff --git a/lib/screens/contribute_screen.dart b/lib/screens/contribute_screen.dart index 973a7b3..f03410e 100644 --- a/lib/screens/contribute_screen.dart +++ b/lib/screens/contribute_screen.dart @@ -104,7 +104,9 @@ class _ContributeScreenState extends State super.initState(); PostHogService.instance.screen('Contribute'); WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().loadAll(); + final p = context.read(); + p.loadAll(); + p.loadLeaderboard(); // independent — always fires regardless of loadAll TTL }); } @@ -274,7 +276,7 @@ class _ContributeScreenState extends State const SizedBox(height: 16), // Leaderboard List - if (provider.isLoading && leaderboard.isEmpty) + if (provider.isLeaderboardLoading && leaderboard.isEmpty) const Padding( padding: EdgeInsets.symmetric(vertical: 60), child: Center(child: CircularProgressIndicator(strokeWidth: 2)),