275 lines
8.1 KiB
Dart
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();
|
|
}
|
|
} |