-
Notifications
You must be signed in to change notification settings - Fork 0
5 logic gamification competitive leaderboard #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9fc61c2
ad1444a
39d52c6
9e18fa4
6d2b5c4
feff29d
e635140
81a4b27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,13 @@ | ||||||||||||
| import Flutter | ||||||||||||
| import UIKit | ||||||||||||
|
|
||||||||||||
| import GoogleMaps | ||||||||||||
| @main | ||||||||||||
| @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { | ||||||||||||
| override func application( | ||||||||||||
| _ application: UIApplication, | ||||||||||||
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | ||||||||||||
| ) -> Bool { | ||||||||||||
| GMSServices.provideAPIKey("YOUR_iOS_API_KEY_HERE") | ||||||||||||
|
||||||||||||
| GMSServices.provideAPIKey("YOUR_iOS_API_KEY_HERE") | |
| if let apiKey = Bundle.main.object(forInfoDictionaryKey: "GOOGLE_MAPS_API_KEY") as? String, | |
| !apiKey.isEmpty { | |
| GMSServices.provideAPIKey(apiKey) | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,4 +22,19 @@ class AuthRepository { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> logout() async { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await auth.signOut(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> changePassword({required String currentPassword, required String newPassword}) async { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final user = FirebaseAuth.instance.currentUser; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (user == null || user.email == null) throw "User not found"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. Re-authenticate the user (Required by Firebase for security) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AuthCredential credential = EmailAuthProvider.credential( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: user.email!, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| password: currentPassword, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await user.reauthenticateWithCredential(credential); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. Update to new password | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await user.updatePassword(newPassword); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
40
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<void> changePassword({required String currentPassword, required String newPassword}) async { | |
| final user = FirebaseAuth.instance.currentUser; | |
| if (user == null || user.email == null) throw "User not found"; | |
| // 1. Re-authenticate the user (Required by Firebase for security) | |
| AuthCredential credential = EmailAuthProvider.credential( | |
| email: user.email!, | |
| password: currentPassword, | |
| ); | |
| await user.reauthenticateWithCredential(credential); | |
| // 2. Update to new password | |
| await user.updatePassword(newPassword); | |
| } | |
| } | |
| Future<void> changePassword({ | |
| required String currentPassword, | |
| required String newPassword, | |
| }) async { | |
| final user = auth.currentUser; | |
| if (user == null || user.email == null) { | |
| throw Exception('User not found'); | |
| } | |
| // 1. Re-authenticate the user (Required by Firebase for security) | |
| final AuthCredential credential = EmailAuthProvider.credential( | |
| email: user.email!, | |
| password: currentPassword, | |
| ); | |
| await user.reauthenticateWithCredential(credential); | |
| // 2. Update to new password | |
| await user.updatePassword(newPassword); | |
| } | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,6 @@ import 'dart:async'; | |||||||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||||||||
| import 'package:cloud_firestore/cloud_firestore.dart'; | ||||||||||||
| import 'package:firebase_auth/firebase_auth.dart'; | ||||||||||||
| import '../models/user_model.dart'; | ||||||||||||
|
|
||||||||||||
| // Use a simple enum for the filters | ||||||||||||
| enum AnalyticsFilter { week, month, allTime } | ||||||||||||
|
|
@@ -30,9 +29,8 @@ class AnalyticsController extends AsyncNotifier<Map<String, double>> { | |||||||||||
| // INSTANT: Fetch from Category Counter in User Document | ||||||||||||
| final doc = await FirebaseFirestore.instance.collection('users').doc(user.uid).get(); | ||||||||||||
| final data = doc.data()?['categoryCounts'] as Map<String, dynamic>? ?? {}; | ||||||||||||
|
|
||||||||||||
| // Convert Map<String, int> to Map<String, double> for the UI bars | ||||||||||||
| return data.map((key, value) => MapEntry(key, (value as num).toDouble())); | ||||||||||||
| final Map<String, dynamic> rawData = Map<String, dynamic>.from(data); | ||||||||||||
| return Map<String, double>.from(rawData.map((key, value) => MapEntry(key, (value as num).toDouble()))); | ||||||||||||
|
Comment on lines
+32
to
+33
|
||||||||||||
| final Map<String, dynamic> rawData = Map<String, dynamic>.from(data); | |
| return Map<String, double>.from(rawData.map((key, value) => MapEntry(key, (value as num).toDouble()))); | |
| return Map<String, double>.from( | |
| data.map((key, value) => MapEntry(key, (value as num).toDouble())), | |
| ); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:flutter_map/flutter_map.dart'; // 🌟 CHANGED: Replaced Google Maps | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:latlong2/latlong.dart'; // 🌟 CHANGED: Using free LatLng | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:geolocator/geolocator.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../models/recycleingcenter_model.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import '../repositories/centers_repository.dart'; // Make sure this path matches your repo | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| final centersProvider = StreamNotifierProvider<CentersController, List<RecyclingCenterModel>>(() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return CentersController(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| class CentersController extends StreamNotifier<List<RecyclingCenterModel>> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 🌟 CHANGED: Initialized the OpenStreetMap controller | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| final MapController mapController = MapController(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| String selectedCategory = "All"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Stream<List<RecyclingCenterModel>> build() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ref.read(centersRepositoryProvider).getCentersStream(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| void setFilter(String category) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectedCategory = category; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ref.invalidateSelf(); // Refresh the stream and UI | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
+25
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 🌟 CHANGED: Using flutter_map's `.move()` syntax instead of animateCamera | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| void animateToCenter(double lat, double lng) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| mapController.move(LatLng(lat, lng), 15.0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Future<Position> getCurrentLocation() async { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!serviceEnabled) return Future.error('Location services are disabled.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| LocationPermission permission = await Geolocator.checkPermission(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (permission == LocationPermission.denied) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| permission = await Geolocator.requestPermission(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+39
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!serviceEnabled) return Future.error('Location services are disabled.'); | |
| LocationPermission permission = await Geolocator.checkPermission(); | |
| if (permission == LocationPermission.denied) { | |
| permission = await Geolocator.requestPermission(); | |
| } | |
| if (!serviceEnabled) { | |
| return Future.error('Location services are disabled.'); | |
| } | |
| LocationPermission permission = await Geolocator.checkPermission(); | |
| if (permission == LocationPermission.denied) { | |
| permission = await Geolocator.requestPermission(); | |
| } | |
| if (permission == LocationPermission.denied) { | |
| return Future.error('Location permissions are denied'); | |
| } | |
| if (permission == LocationPermission.deniedForever) { | |
| return Future.error( | |
| 'Location permissions are permanently denied, we cannot request permissions.', | |
| ); | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import '../models/scan_model.dart'; | ||
| import '../repositories/history_repository.dart'; | ||
|
|
||
| final scanHistoryProvider = StreamNotifierProvider<HistoryController, List<ScanModel>>(() { | ||
| return HistoryController(); | ||
| }); | ||
|
|
||
| class HistoryController extends StreamNotifier<List<ScanModel>> { | ||
| @override | ||
| Stream<List<ScanModel>> build() { | ||
| return ref.read(historyRepositoryProvider).getScanHistoryStream(); | ||
| } | ||
|
|
||
| // Logic for the Floating Card metric | ||
| int getTodayScanCount(List<ScanModel> scans) { | ||
| final now = DateTime.now(); | ||
| return scans.where((scan) { | ||
| final date = scan.timestamp.toDate(); | ||
| return date.year == now.year && | ||
| date.month == now.month && | ||
| date.day == now.day; | ||
| }).length; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,36 @@ | ||||||||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||||||||
| import 'package:firebase_auth/firebase_auth.dart'; | ||||||||||||
| import '../models/user_model.dart'; | ||||||||||||
| import '../repositories/user_repository.dart'; | ||||||||||||
|
|
||||||||||||
| final userControllerProvider = | ||||||||||||
| StreamProvider.family<UserModel, String>((ref, uid) { | ||||||||||||
| // 🟢 DATA PROVIDER: Keep this as StreamProvider so your UI doesn't break | ||||||||||||
| final userControllerProvider = StreamProvider.family<UserModel, String>((ref, uid) { | ||||||||||||
| final repo = ref.watch(userRepositoryProvider); | ||||||||||||
| return repo.getUser(uid); | ||||||||||||
| }); | ||||||||||||
|
|
||||||||||||
| // 🔵 ACTION PROVIDER: Use this for updating profile settings | ||||||||||||
| final userProfileActionsProvider = Provider((ref) => UserProfileActions(ref)); | ||||||||||||
|
|
||||||||||||
| class UserProfileActions { | ||||||||||||
| final Ref ref; | ||||||||||||
| UserProfileActions(this.ref); | ||||||||||||
|
|
||||||||||||
| Future<void> updateProfile({ | ||||||||||||
| required String username, | ||||||||||||
| required String email, | ||||||||||||
| String? profileUrl, | ||||||||||||
| }) async { | ||||||||||||
| final uid = FirebaseAuth.instance.currentUser?.uid; | ||||||||||||
| if (uid == null) return; | ||||||||||||
|
|
||||||||||||
| final updates = { | ||||||||||||
| 'username': username, | ||||||||||||
| 'email': email, | ||||||||||||
| // Use the null-aware spread as suggested by your diagnostic | ||||||||||||
| ...?profileUrl != null ? {'profileurl': profileUrl} : null, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
|
Comment on lines
+30
to
+33
|
||||||||||||
| // Use the null-aware spread as suggested by your diagnostic | |
| ...?profileUrl != null ? {'profileurl': profileUrl} : null, | |
| }; | |
| if (profileUrl != null) 'profileurl': profileUrl, | |
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import 'package:cloud_firestore/cloud_firestore.dart'; | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import '../models/recycleingcenter_model.dart'; | ||
|
|
||
| final centersRepositoryProvider = Provider((ref) => CentersRepository()); | ||
|
|
||
| class CentersRepository { | ||
| final FirebaseFirestore _firestore = FirebaseFirestore.instance; | ||
|
|
||
| Stream<List<RecyclingCenterModel>> getCentersStream() { | ||
| return _firestore | ||
| .collection('recyclingCenters') | ||
| .snapshots() | ||
| .map((snapshot) => snapshot.docs | ||
| .map((doc) => RecyclingCenterModel.fromMap(doc.data())) | ||
| .toList()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import 'package:cloud_firestore/cloud_firestore.dart'; | ||
| import 'package:firebase_auth/firebase_auth.dart'; | ||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||
| import '../models/scan_model.dart'; | ||
|
|
||
| final historyRepositoryProvider = Provider((ref) => HistoryRepository()); | ||
|
|
||
| class HistoryRepository { | ||
| final FirebaseFirestore _firestore = FirebaseFirestore.instance; | ||
| final FirebaseAuth _auth = FirebaseAuth.instance; | ||
|
|
||
| Stream<List<ScanModel>> getScanHistoryStream() { | ||
| final uid = _auth.currentUser?.uid; | ||
| if (uid == null) return Stream.value([]); | ||
|
|
||
| // Streams the specific user's scan sub-collection in real-time | ||
| return _firestore | ||
| .collection('users') | ||
| .doc(uid) | ||
| .collection('scans') | ||
| .orderBy('timestamp', descending: true) // Newest first | ||
| .snapshots() | ||
| .map((snapshot) { | ||
| return snapshot.docs.map((doc) => ScanModel.fromMap(doc.data())).toList(); | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Shaam143 API Key Google maps (IOS)