release: bump version to 1.4(p) (versionCode 14)
- Update versionCode 12 → 14, versionName 1.3(p) → 1.4(p) - Update pubspec.yaml version to 1.4.0+14 - Add CHANGELOG.md with full version history - Update README.md: version badge + changelog section - Desktop Contribute Dashboard rebuilt to match web version - Contributor Dashboard title, 3-tab nav (Contribute/Leaderboard/Achievements) - Two-column submit form, tier milestone progress bar - Desktop leaderboard with podium, filters, rank table (green points) - Desktop achievements 3-column badge grid - Inline Reward Shop with RP balance - Gamification feature module (EP, RP, leaderboard, achievements, shop) - Profile screen redesigned to match web app layout with animations - Home screen bottom sheet date filter chips - Updated API endpoints, login/event detail screens, theme colors - Added Gilroy font suite, responsive layout improvements
This commit is contained in:
124
lib/features/gamification/providers/gamification_provider.dart
Normal file
124
lib/features/gamification/providers/gamification_provider.dart
Normal file
@@ -0,0 +1,124 @@
|
||||
// lib/features/gamification/providers/gamification_provider.dart
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import '../models/gamification_models.dart';
|
||||
import '../services/gamification_service.dart';
|
||||
|
||||
class GamificationProvider extends ChangeNotifier {
|
||||
final GamificationService _service = GamificationService();
|
||||
|
||||
// State
|
||||
UserGamificationProfile? profile;
|
||||
List<LeaderboardEntry> leaderboard = [];
|
||||
List<ShopItem> shopItems = [];
|
||||
List<AchievementBadge> achievements = [];
|
||||
|
||||
// Leaderboard filters — matches web version
|
||||
String leaderboardDistrict = 'Overall Kerala';
|
||||
String leaderboardTimePeriod = 'all_time'; // 'all_time' | 'this_month'
|
||||
|
||||
bool isLoading = false;
|
||||
String? error;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Load everything at once (called when ContributeScreen is mounted)
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<void> loadAll() async {
|
||||
isLoading = true;
|
||||
error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final results = await Future.wait([
|
||||
_service.getProfile(),
|
||||
_service.getLeaderboard(district: leaderboardDistrict, timePeriod: leaderboardTimePeriod),
|
||||
_service.getShopItems(),
|
||||
_service.getAchievements(),
|
||||
]);
|
||||
|
||||
profile = results[0] as UserGamificationProfile;
|
||||
leaderboard = results[1] as List<LeaderboardEntry>;
|
||||
shopItems = results[2] as List<ShopItem>;
|
||||
achievements = results[3] as List<AchievementBadge>;
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
} finally {
|
||||
isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Change district filter
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<void> setDistrict(String district) async {
|
||||
if (leaderboardDistrict == district) return;
|
||||
leaderboardDistrict = district;
|
||||
notifyListeners();
|
||||
try {
|
||||
leaderboard = await _service.getLeaderboard(district: district, timePeriod: leaderboardTimePeriod);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Change time period filter
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<void> setTimePeriod(String period) async {
|
||||
if (leaderboardTimePeriod == period) return;
|
||||
leaderboardTimePeriod = period;
|
||||
notifyListeners();
|
||||
try {
|
||||
leaderboard = await _service.getLeaderboard(district: leaderboardDistrict, timePeriod: period);
|
||||
} catch (e) {
|
||||
error = e.toString();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Redeem a shop item — deducts RP locally optimistically, returns voucher code
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<String> redeemItem(String itemId) async {
|
||||
final item = shopItems.firstWhere((s) => s.id == itemId);
|
||||
|
||||
// Optimistically deduct RP
|
||||
if (profile != null) {
|
||||
profile = UserGamificationProfile(
|
||||
userId: profile!.userId,
|
||||
lifetimeEp: profile!.lifetimeEp,
|
||||
currentEp: profile!.currentEp,
|
||||
currentRp: profile!.currentRp - item.rpCost,
|
||||
tier: profile!.tier,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
try {
|
||||
final record = await _service.redeemItem(itemId);
|
||||
return record.voucherCode;
|
||||
} catch (e) {
|
||||
// Rollback on failure
|
||||
if (profile != null) {
|
||||
profile = UserGamificationProfile(
|
||||
userId: profile!.userId,
|
||||
lifetimeEp: profile!.lifetimeEp,
|
||||
currentEp: profile!.currentEp,
|
||||
currentRp: profile!.currentRp + item.rpCost,
|
||||
tier: profile!.tier,
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Submit a contribution
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<void> submitContribution(Map<String, dynamic> data) async {
|
||||
await _service.submitContribution(data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user