Insparkspokalen-ui/lib/services/teamUserSyncService.dart
2025-05-22 13:56:05 +02:00

275 lines
8.1 KiB
Dart

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:insparkspokalen_ui/models/teamModel.dart';
import 'package:insparkspokalen_ui/models/userModel.dart';
/// A service to synchronize team and user data with improved synchronization
class TeamUserSyncService with ChangeNotifier {
// Singleton pattern
static final TeamUserSyncService _instance = TeamUserSyncService._internal();
factory TeamUserSyncService() => _instance;
TeamUserSyncService._internal();
// Base URLs
final String baseUrlTeam = 'https://group-10-15.pvt.dsv.su.se/team';
final String baseUrlUser = 'https://group-10-15.pvt.dsv.su.se/user';
// Current data
TeamModel? _currentTeam;
UserModel? _currentUser;
List<TeamModel> _allTeams = [];
// Flag to track ongoing operations to prevent redundant requests
bool _isSyncing = false;
// Getters
TeamModel? get currentTeam => _currentTeam;
UserModel? get currentUser => _currentUser;
List<TeamModel> get allTeams => _allTeams;
bool get isSyncing => _isSyncing;
// Set current user with improved team synchronization
Future<void> setCurrentUser(UserModel user) async {
_currentUser = user;
notifyListeners();
// When user is set, attempt to fetch their team
if (user != null) {
await syncUserTeamData(user.email);
}
}
// Comprehensive synchronization of user team data
Future<void> syncUserTeamData(String email) async {
if (_isSyncing) return;
_isSyncing = true;
try {
// 1. Get the user's team ID
final teamId = await fetchUserTeam(email);
// 2. If teamId exists, fetch the complete team data
if (teamId != null) {
await fetchTeamById(teamId);
} else {
// Clear current team if user doesn't belong to any team
_currentTeam = null;
notifyListeners();
}
// 3. Always fetch all teams to keep the leaderboard updated
await fetchAllTeams();
} finally {
_isSyncing = false;
}
}
// Fetch the current user's team ID
Future<int?> fetchUserTeam(String email) async {
final url = Uri.parse('$baseUrlUser/get-user-team/$email');
try {
final response = await http.get(url);
if (response.statusCode == 200 && response.body.isNotEmpty) {
// Handle potential JSON format from the API
var responseData = response.body;
// Check if the response is JSON
try {
if (responseData.startsWith('{') || responseData.startsWith('[')) {
var jsonData = jsonDecode(responseData);
if (jsonData is Map && jsonData.containsKey('teamId')) {
return jsonData['teamId'] as int;
}
}
} catch (_) {
// Not JSON, continue with normal parsing
}
// Try parsing as a simple integer
try {
return int.parse(responseData);
} catch (e) {
print('Error parsing team ID: $e');
return null;
}
}
} catch (e) {
print('Error fetching user team: $e');
}
return null;
}
// Fetch a team by ID with improved error handling
Future<TeamModel?> fetchTeamById(int teamId) async {
final url = Uri.parse('$baseUrlTeam/get-team/$teamId');
try {
final response = await http.get(url);
if (response.statusCode == 200 && response.body.isNotEmpty) {
final teamData = jsonDecode(response.body);
_currentTeam = TeamModel.fromJson(teamData);
notifyListeners();
return _currentTeam;
} else if (response.statusCode == 404) {
// Team not found, clear current team
_currentTeam = null;
notifyListeners();
}
} catch (e) {
print('Error fetching team: $e');
}
return null;
}
// Fetch all teams with improved sorting
Future<List<TeamModel>> fetchAllTeams() async {
final url = Uri.parse('$baseUrlTeam/all');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
_allTeams = data.map((json) => TeamModel.fromJson(json)).toList();
// Sort teams by score (descending) for leaderboard display
_allTeams.sort((a, b) => b.score.compareTo(a.score));
notifyListeners();
return _allTeams;
}
} catch (e) {
print('Error fetching all teams: $e');
}
return [];
}
// Join a team with no restriction checks - modified for testing
Future<bool> joinTeam(int teamId) async {
if (_currentUser == null || _isSyncing) return false;
// REMOVED: Check if user is already in a team - for testing purposes
_isSyncing = true;
try {
// 1. Update UserMS (update user's teamId)
final userUrl = Uri.parse('$baseUrlUser/join-team');
final userResponse = await http.post(
userUrl,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': _currentUser!.email,
'teamId': teamId,
}),
);
if (userResponse.statusCode != 200 && userResponse.statusCode != 201) {
print('Failed to update user team: ${userResponse.statusCode} - ${userResponse.body}');
return false;
}
// 2. Update TeamMS (add user to team's userEmails list)
final teamUrl = Uri.parse('$baseUrlTeam/add-user-to-team');
final teamResponse = await http.post(
teamUrl,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': _currentUser!.email,
'teamId': teamId,
}),
);
if (teamResponse.statusCode != 200 && teamResponse.statusCode != 201) {
print('Failed to add user to team: ${teamResponse.statusCode} - ${teamResponse.body}');
return false;
}
// 3. If both successful, update local data
await syncUserTeamData(_currentUser!.email);
return true;
} catch (e) {
print('Error joining team: $e');
return false;
} finally {
_isSyncing = false;
}
}
// Leave a team with improved synchronization
Future<bool> leaveTeam() async {
if (_currentUser == null || _isSyncing) return false;
// REMOVED: Check if user is in a team - for testing purposes
// Get current team ID from user data instead
final userTeamId = await fetchUserTeam(_currentUser!.email);
if (userTeamId == null) return false;
_isSyncing = true;
try {
// 1. Remove user from team in TeamMS
final teamUrl = Uri.parse('$baseUrlTeam/remove-user-from-team/${_currentUser!.email}/$userTeamId');
final teamResponse = await http.delete(teamUrl);
if (teamResponse.statusCode != 200) {
print('Failed to remove user from team: ${teamResponse.statusCode} - ${teamResponse.body}');
}
// 2. Update UserMS by setting teamId to null
final userUrl = Uri.parse('$baseUrlUser/join-team');
final userResponse = await http.post(
userUrl,
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': _currentUser!.email,
'teamId': null,
}),
);
if (userResponse.statusCode != 200 && userResponse.statusCode != 201) {
print('Failed to update user record: ${userResponse.statusCode} - ${userResponse.body}');
return false;
}
// 3. Update local data
_currentTeam = null;
// Refresh all teams to reflect the changes
await fetchAllTeams();
notifyListeners();
return true;
} catch (e) {
print('Error leaving team: $e');
return false;
} finally {
_isSyncing = false;
}
}
// Check if the current user is in a team
bool isUserInTeam() {
return _currentTeam != null;
}
// Get team members (emails) for the current team
List<String> getCurrentTeamMembers() {
return _currentTeam?.userEmails ?? [];
}
// Clear current data (e.g., on logout)
void clearData() {
_currentTeam = null;
_currentUser = null;
_allTeams = [];
notifyListeners();
}
}