Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:athletix/views/screens/athlete/athlete_dashboard.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'views/screens/splash_screen.dart';
import 'views/screens/connection_requests_screen.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -22,7 +23,6 @@ class MyApp extends StatelessWidget {
useMaterial3: true,
fontFamily: 'Montserrat',
),
home: SplashScreen(),
);
}
}
193 changes: 193 additions & 0 deletions lib/services/connection_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

class ConnectionService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;

/// Get pending connection requests for an athlete
Stream<List<Map<String, dynamic>>> getConnectionRequests() {
final uid = _auth.currentUser?.uid;
if (uid == null) return const Stream.empty();

return _firestore
.collection('connection_requests')
.where('athleteId', isEqualTo: uid)
.where('status', isEqualTo: 'pending')
.orderBy('createdAt', descending: true)
.snapshots()
.map((snapshot) => snapshot.docs.map((doc) {
final data = doc.data();
data['id'] = doc.id;
return data;
}).toList());
}

/// Accept a connection request
Future<bool> acceptConnectionRequest(String requestId, Map<String, dynamic> requestData) async {
try {
final uid = _auth.currentUser?.uid;
if (uid == null) return false;

final batch = _firestore.batch();

// Update request status
batch.update(
_firestore.collection('connection_requests').doc(requestId),
{'status': 'accepted', 'updatedAt': FieldValue.serverTimestamp()},
);

// Add player to organization's connected players
batch.set(
_firestore
.collection('organization_players')
.doc(requestData['organizationId'])
.collection('connected_players')
.doc(uid),
{
'connectedAt': FieldValue.serverTimestamp(),
'status': 'active',
},
);

// Add organization to player's connected organizations
batch.set(
_firestore
.collection('player_organizations')
.doc(uid)
.collection('connected_organizations')
.doc(requestData['organizationId']),
{
'connectedAt': FieldValue.serverTimestamp(),
'status': 'active',
},
);

// Create notification for organization
batch.add(
_firestore
.collection('notifications')
.doc(requestData['organizationId'])
.collection('user_notifications'),
{
'title': 'Connection Accepted',
'message': '${requestData['athleteName']} accepted your connection request',
'type': 'connection_accepted',
'athleteId': uid,
'createdAt': FieldValue.serverTimestamp(),
'read': false,
},
);

await batch.commit();
return true;
} catch (e) {
print('Error accepting connection request: $e');
return false;
}
}

/// Reject a connection request
Future<bool> rejectConnectionRequest(String requestId, Map<String, dynamic> requestData) async {
try {
final uid = _auth.currentUser?.uid;
if (uid == null) return false;

final batch = _firestore.batch();

// Update request status
batch.update(
_firestore.collection('connection_requests').doc(requestId),
{'status': 'rejected', 'updatedAt': FieldValue.serverTimestamp()},
);

// Create notification for organization
batch.add(
_firestore
.collection('notifications')
.doc(requestData['organizationId'])
.collection('user_notifications'),
{
'title': 'Connection Rejected',
'message': '${requestData['athleteName']} rejected your connection request',
'type': 'connection_rejected',
'athleteId': uid,
'createdAt': FieldValue.serverTimestamp(),
'read': false,
},
);

await batch.commit();
return true;
} catch (e) {
print('Error rejecting connection request: $e');
return false;
}
}

/// Get connected organizations for an athlete
Stream<List<Map<String, dynamic>>> getConnectedOrganizations() {
final uid = _auth.currentUser?.uid;
if (uid == null) return const Stream.empty();

return _firestore
.collection('player_organizations')
.doc(uid)
.collection('connected_organizations')
.snapshots()
.asyncMap((snapshot) async {
List<Map<String, dynamic>> organizations = [];
for (var doc in snapshot.docs) {
try {
final orgDoc = await _firestore
.collection('users')
.doc(doc.id)
.get();
if (orgDoc.exists) {
final orgData = orgDoc.data() as Map<String, dynamic>;
orgData['id'] = doc.id;
orgData['connectedAt'] = doc.data()['connectedAt'];
organizations.add(orgData);
}
} catch (e) {
print('Error loading organization: $e');
}
}
return organizations;
});
}

/// Disconnect from an organization
Future<bool> disconnectFromOrganization(String organizationId) async {
try {
final uid = _auth.currentUser?.uid;
if (uid == null) return false;

final batch = _firestore.batch();

// Remove from organization's connected players
batch.delete(
_firestore
.collection('organization_players')
.doc(organizationId)
.collection('connected_players')
.doc(uid),
);

// Remove from player's connected organizations
batch.delete(
_firestore
.collection('player_organizations')
.doc(uid)
.collection('connected_organizations')
.doc(organizationId),
);

await batch.commit();
return true;
} catch (e) {
print('Error disconnecting from organization: $e');
return false;
}
}
}
12 changes: 9 additions & 3 deletions lib/views/screens/athlete/athlete_dashboard.dart
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ class _DashboardScreenState extends State<DashboardScreen>
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF667EEA),
color: const Color(0xFF667EEA),
),
),
],
Expand Down Expand Up @@ -418,13 +418,19 @@ class _DashboardScreenState extends State<DashboardScreen>
),
const SizedBox(height: 20),
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
padding: const EdgeInsets.all(16),
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.1,
children: [
_buildDashboardCard(
context,
'Connection Requests',
Icons.group_add,
Colors.orange,
() => Navigator.pushNamed(context, '/connection-requests'),
),
_buildEnhancedActionCard(
icon: Icons.healing_rounded,
label: "Injury Tracker",
Expand Down
Loading