Initial commit: unionflow-mobile-apps

Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 16:30:08 +00:00
commit d094d6db9c
1790 changed files with 507435 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
import 'package:unionflow_mobile_apps/core/utils/logger.dart';
import '../../domain/entities/network_item.dart';
/// Repository pour la recherche réseau (membres + organisations).
/// Délègue la recherche au backend Quarkus.
@lazySingleton
class NetworkRepository {
final ApiClient _apiClient;
NetworkRepository(this._apiClient);
List<dynamic> _parseListResponse(dynamic data) {
if (data is List) return data;
if (data is Map && data.containsKey('content')) {
final content = data['content'];
return content is List ? content : [];
}
return [];
}
/// Recherche de membres (GET /api/membres/recherche?q=...)
Future<List<NetworkItem>> searchMembers(String query, {int page = 0, int size = 20}) async {
if (query.trim().isEmpty) return [];
try {
final response = await _apiClient.get(
'/api/membres/recherche',
queryParameters: {'q': query.trim(), 'page': page, 'size': size},
);
final data = _parseListResponse(response.data);
return data.map((json) => _memberFromJson(json as Map<String, dynamic>)).toList();
} on DioException catch (e) {
if (e.response?.statusCode == 400) return [];
rethrow;
}
}
/// Recherche d'organisations (GET /api/organisations/recherche?nom=...)
Future<List<NetworkItem>> searchOrganizations(String query, {int page = 0, int size = 20}) async {
if (query.trim().isEmpty) return [];
try {
final response = await _apiClient.get(
'/api/organisations/recherche',
queryParameters: {'nom': query.trim(), 'page': page, 'size': size},
);
final data = _parseListResponse(response.data);
return data.map((json) => _organisationFromJson(json as Map<String, dynamic>)).toList();
} on DioException catch (e) {
if (e.response?.statusCode == 400) return [];
rethrow;
}
}
/// Recherche globale : membres + organisations (deux appels parallèles).
/// Si [followedIds] est fourni, les membres dont l'id est dans le set auront [isConnected: true].
Future<List<NetworkItem>> search(String query, {int page = 0, int size = 10, Set<String>? followedIds}) async {
if (query.trim().isEmpty) return [];
try {
final results = await Future.wait([
searchMembers(query, page: page, size: size),
searchOrganizations(query, page: page, size: size),
]);
final members = results[0].map((m) {
if (followedIds != null && followedIds.contains(m.id)) return m.copyWith(isConnected: true);
return m;
}).toList();
final orgs = results[1];
return [...members, ...orgs];
} catch (e) {
rethrow;
}
}
/// Liste des ids des membres suivis par l'utilisateur connecté (GET /api/membres/me/suivis).
Future<List<String>> getFollowedIds() async {
try {
final response = await _apiClient.get('/api/membres/me/suivis');
if (response.statusCode != 200) return [];
final data = response.data;
if (data is! List) return [];
return data.map((e) => e.toString()).toList();
} on DioException catch (e) {
if (e.response?.statusCode == 401 || e.response?.statusCode == 403) return [];
AppLogger.error('NetworkRepository: getFollowedIds échoué', error: e);
rethrow;
}
}
/// Suivre un membre (POST /api/membres/{id}/suivre). Retourne true si following après l'appel.
Future<bool> follow(String memberId) async {
try {
final response = await _apiClient.post('/api/membres/$memberId/suivre');
if (response.statusCode == 200 && response.data is Map) {
return (response.data as Map)['following'] == true;
}
return false;
} on DioException catch (e, st) {
AppLogger.error('NetworkRepository: follow échoué', error: e, stackTrace: st);
rethrow;
}
}
/// Ne plus suivre un membre (DELETE /api/membres/{id}/suivre). Retourne false (plus suivi).
Future<bool> unfollow(String memberId) async {
try {
final response = await _apiClient.delete('/api/membres/$memberId/suivre');
if (response.statusCode == 200 && response.data is Map) {
return (response.data as Map)['following'] == true;
}
return false;
} on DioException catch (e, st) {
AppLogger.error('NetworkRepository: unfollow échoué', error: e, stackTrace: st);
rethrow;
}
}
static NetworkItem _memberFromJson(Map<String, dynamic> json) {
final id = json['id']?.toString() ?? '';
final prenom = json['prenom']?.toString() ?? '';
final nom = json['nom']?.toString() ?? '';
final name = '$prenom $nom'.trim().isEmpty ? (json['numeroMembre']?.toString() ?? id) : '$prenom $nom'.trim();
return NetworkItem(
id: id,
name: name,
subtitle: json['profession']?.toString() ?? json['statutCompteLibelle']?.toString(),
avatarUrl: null,
type: 'Member',
isConnected: false,
);
}
static NetworkItem _organisationFromJson(Map<String, dynamic> json) {
final id = json['id']?.toString() ?? '';
return NetworkItem(
id: id,
name: json['nom']?.toString() ?? json['nomCourt']?.toString() ?? 'Organisation',
subtitle: json['typeOrganisationLibelle']?.toString() ?? json['statutLibelle']?.toString(),
avatarUrl: null,
type: 'Organization',
isConnected: false,
);
}
}