-
Notifications
You must be signed in to change notification settings - Fork 2
added sessions , profile page and routed auth and onboarding #11
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
Open
chitrakshbotwala
wants to merge
3
commits into
main
Choose a base branch
from
onboarding
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import 'package:firebase_auth/firebase_auth.dart'; | ||
| import 'firebase_service.dart'; | ||
|
|
||
| /// Firebase Authentication Service | ||
| /// Uses the central FirebaseService instance for all auth operations. | ||
| class FirebaseAuthService { | ||
| final FirebaseAuth _auth = FirebaseService.instance.auth; | ||
|
|
||
| User? get currentUser => _auth.currentUser; | ||
|
|
||
| bool get isAuthenticated => currentUser != null; | ||
|
|
||
| String? get userId => currentUser?.uid; | ||
|
|
||
| Stream<User?> get authStateChanges => _auth.authStateChanges(); | ||
|
|
||
| Future<UserCredential> signUp({ | ||
| required String email, | ||
| required String password, | ||
| }) async { | ||
| return await _auth.createUserWithEmailAndPassword( | ||
| email: email, | ||
| password: password, | ||
| ); | ||
| } | ||
|
|
||
| Future<UserCredential> signIn({ | ||
| required String email, | ||
| required String password, | ||
| }) async { | ||
| return await _auth.signInWithEmailAndPassword( | ||
| email: email, | ||
| password: password, | ||
| ); | ||
| } | ||
|
|
||
| Future<void> sendPasswordResetEmail({required String email}) async { | ||
| await _auth.sendPasswordResetEmail(email: email); | ||
| } | ||
|
|
||
| Future<void> confirmPasswordReset({ | ||
| required String code, | ||
| required String newPassword, | ||
| }) async { | ||
| await _auth.confirmPasswordReset(code: code, newPassword: newPassword); | ||
| } | ||
|
|
||
| Future<void> updatePassword({required String newPassword}) async { | ||
| await currentUser?.updatePassword(newPassword); | ||
| } | ||
|
|
||
| Future<void> sendEmailVerification() async { | ||
| await currentUser?.sendEmailVerification(); | ||
| } | ||
|
|
||
| Future<void> signOut() async { | ||
| await _auth.signOut(); | ||
| } | ||
|
|
||
| Future<void> deleteAccount() async { | ||
| await currentUser?.delete(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| import 'package:firebase_core/firebase_core.dart'; | ||
| import 'package:firebase_auth/firebase_auth.dart'; | ||
| import 'package:firebase_database/firebase_database.dart'; | ||
|
|
||
| /// Central Firebase service that provides singleton instances | ||
| /// of all Firebase services used in the app. | ||
| /// | ||
| /// This ensures we use pre-initialized instances and avoid | ||
| /// creating multiple connections to Firebase. | ||
| class FirebaseService { | ||
| // Private constructor for singleton pattern | ||
| FirebaseService._(); | ||
|
|
||
| // Singleton instance | ||
| static final FirebaseService _instance = FirebaseService._(); | ||
| static FirebaseService get instance => _instance; | ||
|
|
||
| // Firebase instances (lazy initialized after Firebase.initializeApp()) | ||
| FirebaseAuth? _auth; | ||
| FirebaseDatabase? _database; | ||
|
|
||
| /// Initialize Firebase and cache the instances | ||
| static Future<void> initialize() async { | ||
| await Firebase.initializeApp(); | ||
| _instance._auth = FirebaseAuth.instance; | ||
| _instance._database = FirebaseDatabase.instance; | ||
| } | ||
|
|
||
| /// Get the FirebaseAuth instance | ||
| FirebaseAuth get auth { | ||
| if (_auth == null) { | ||
| throw Exception( | ||
| 'FirebaseService not initialized. Call FirebaseService.initialize() first.', | ||
| ); | ||
| } | ||
| return _auth!; | ||
| } | ||
|
|
||
| /// Get the FirebaseDatabase instance | ||
| FirebaseDatabase get database { | ||
| if (_database == null) { | ||
| throw Exception( | ||
| 'FirebaseService not initialized. Call FirebaseService.initialize() first.', | ||
| ); | ||
| } | ||
| return _database!; | ||
| } | ||
|
|
||
| // Convenience getters | ||
| User? get currentUser => auth.currentUser; | ||
| bool get isAuthenticated => currentUser != null; | ||
| String? get userId => currentUser?.uid; | ||
| Stream<User?> get authStateChanges => auth.authStateChanges(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| import 'package:firebase_database/firebase_database.dart'; | ||
| import 'package:vector/models/session_tracking_model.dart'; | ||
| import 'package:vector/core/constants.dart'; | ||
| import 'package:vector/core/services/firebase_service.dart'; | ||
|
|
||
| /// Service for managing session data in Firebase Realtime Database | ||
| class SessionService { | ||
| final FirebaseDatabase _database = FirebaseService.instance.database; | ||
|
|
||
| DatabaseReference get _sessionsRef => _database.ref(sessionsPath); | ||
|
|
||
| /// Start a new session | ||
| Future<String> startSession({ | ||
| required String userId, | ||
| required ActivityType activityType, | ||
| }) async { | ||
| final session = SessionModel( | ||
| userId: userId, | ||
| activityType: activityType, | ||
| startTime: DateTime.now(), | ||
| isActive: true, | ||
| ); | ||
|
|
||
| final newRef = _sessionsRef.child(userId).push(); | ||
| await newRef.set(session.toJson()); | ||
| return newRef.key!; | ||
| } | ||
|
|
||
| /// Update session with new location data | ||
| Future<void> updateSession({ | ||
| required String userId, | ||
| required String sessionId, | ||
| required SessionModel session, | ||
| }) async { | ||
| await _sessionsRef.child(userId).child(sessionId).update(session.toJson()); | ||
| } | ||
|
|
||
| /// End a session | ||
| Future<void> endSession({ | ||
| required String userId, | ||
| required String sessionId, | ||
| required SessionModel session, | ||
| }) async { | ||
| final endedSession = session.copyWith( | ||
| endTime: DateTime.now(), | ||
| isActive: false, | ||
| ); | ||
| await _sessionsRef | ||
| .child(userId) | ||
| .child(sessionId) | ||
| .set(endedSession.toJson()); | ||
| } | ||
|
|
||
| /// Get all sessions for a user | ||
| Future<List<SessionModel>> getUserSessions(String userId) async { | ||
| final snapshot = await _sessionsRef.child(userId).get(); | ||
|
|
||
| if (!snapshot.exists || snapshot.value == null) { | ||
| return []; | ||
| } | ||
|
|
||
| final data = Map<String, dynamic>.from(snapshot.value as Map); | ||
| return data.entries.map((e) { | ||
| return SessionModel.fromJson( | ||
| Map<String, dynamic>.from(e.value), | ||
| id: e.key, | ||
| ); | ||
| }).toList(); | ||
| } | ||
|
|
||
| /// Get today's sessions for a user | ||
| Future<List<SessionModel>> getTodaySessions(String userId) async { | ||
| final allSessions = await getUserSessions(userId); | ||
| final today = DateTime.now(); | ||
| final startOfDay = DateTime(today.year, today.month, today.day); | ||
|
|
||
| return allSessions.where((s) => s.startTime.isAfter(startOfDay)).toList(); | ||
| } | ||
|
|
||
| /// Get sessions for a specific date range | ||
| Future<List<SessionModel>> getSessionsInRange( | ||
| String userId, { | ||
| required DateTime startDate, | ||
| required DateTime endDate, | ||
| }) async { | ||
| final allSessions = await getUserSessions(userId); | ||
| return allSessions | ||
| .where( | ||
| (s) => | ||
| s.startTime.isAfter(startDate) && | ||
| s.startTime.isBefore(endDate.add(const Duration(days: 1))), | ||
| ) | ||
| .toList() | ||
| ..sort((a, b) => b.startTime.compareTo(a.startTime)); // Most recent first | ||
| } | ||
|
|
||
| /// Get sessions for the past week | ||
| Future<List<SessionModel>> getWeekSessions(String userId) async { | ||
| final now = DateTime.now(); | ||
| final weekAgo = now.subtract(const Duration(days: 7)); | ||
| return getSessionsInRange(userId, startDate: weekAgo, endDate: now); | ||
| } | ||
|
|
||
| /// Get sessions for the past month | ||
| Future<List<SessionModel>> getMonthSessions(String userId) async { | ||
| final now = DateTime.now(); | ||
| final monthAgo = DateTime(now.year, now.month - 1, now.day); | ||
| return getSessionsInRange(userId, startDate: monthAgo, endDate: now); | ||
| } | ||
|
|
||
| /// Get sessions for the past year | ||
| Future<List<SessionModel>> getYearSessions(String userId) async { | ||
| final now = DateTime.now(); | ||
| final yearAgo = DateTime(now.year - 1, now.month, now.day); | ||
| return getSessionsInRange(userId, startDate: yearAgo, endDate: now); | ||
| } | ||
|
|
||
| /// Get the active session if any | ||
| Future<SessionModel?> getActiveSession(String userId) async { | ||
| final sessions = await getUserSessions(userId); | ||
| try { | ||
| return sessions.firstWhere((s) => s.isActive); | ||
| } catch (_) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| /// Delete a session | ||
| Future<void> deleteSession({ | ||
| required String userId, | ||
| required String sessionId, | ||
| }) async { | ||
| await _sessionsRef.child(userId).child(sessionId).remove(); | ||
| } | ||
|
|
||
| /// Calculate calories burned based on activity type and distance | ||
| static double calculateCalories({ | ||
| required ActivityType activityType, | ||
| required double distanceMeters, | ||
| required double weightKg, | ||
| }) { | ||
| // MET values (Metabolic Equivalent of Task) | ||
| // Walking: 3.5 MET, Running: 9.8 MET, Cycling: 7.5 MET | ||
| double met; | ||
| switch (activityType) { | ||
| case ActivityType.walking: | ||
| met = 3.5; | ||
| break; | ||
| case ActivityType.running: | ||
| met = 9.8; | ||
| break; | ||
| case ActivityType.cycling: | ||
| met = 7.5; | ||
| break; | ||
| } | ||
|
|
||
| // Estimate duration based on average speeds | ||
| // Walking: 5 km/h, Running: 10 km/h, Cycling: 20 km/h | ||
| double avgSpeedKmh; | ||
| switch (activityType) { | ||
| case ActivityType.walking: | ||
| avgSpeedKmh = 5.0; | ||
| break; | ||
| case ActivityType.running: | ||
| avgSpeedKmh = 10.0; | ||
| break; | ||
| case ActivityType.cycling: | ||
| avgSpeedKmh = 20.0; | ||
| break; | ||
| } | ||
|
|
||
| final distanceKm = distanceMeters / 1000; | ||
| final durationHours = distanceKm / avgSpeedKmh; | ||
|
|
||
| // Calories = MET × weight (kg) × time (hours) | ||
| return met * weightKg * durationHours; | ||
|
Comment on lines
+157
to
+176
|
||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
ACCESS_BACKGROUND_LOCATION permission requires special justification on Android 10+ and should only be requested if the app truly needs to track location in the background. If background tracking is not implemented, this permission should be removed. Also consider checking if FOREGROUND_SERVICE permission is actually used by implementing a foreground service for tracking.