import 'dart:async'; import 'dart:convert'; import 'dart:developer' as dev; // ignore: depend_on_referenced_packages import 'package:http/http.dart' as http; import 'package:didvan/config/auth_config.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/models/comment/user.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/mention/mention.dart'; import 'package:didvan/models/users_mention.dart'; import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/services/auth/auth_interceptor.dart'; import 'package:didvan/services/auth/token_storage.dart'; import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:flutter/cupertino.dart'; import 'package:provider/provider.dart'; class MentionsState extends CoreProvier { TextEditingController textFieldController = TextEditingController(); TextEditingController searchUsers = TextEditingController(); List mentionedUsers = []; int? commentId; UserOverview? replyingTo; bool showReplyBox = false; bool showUsersForMentionsLayout = false; bool showPrivates = false; bool hideMentionedUser = false; late String type; final List comments = []; final List users = []; int itemId = 0; // UUID محتوای جدید (در صورت وجود) — برای فراخوانی content-service. String? contentUuid; // نگاشت UsersMention.id (hashCode) → keycloak UUID. هنگام ارسال // mention‌ها، از این نگاشت استفاده می‌کنیم تا UUID واقعی فرستاده شود. final Map _userUuidById = {}; Future getComments() async { if (!AuthConfig.useLegacyApi) { await _getCommentsFromCommentsService(); return; } final service = RequestService( RequestHelper.mention(itemId, type), ); await service.httpGet(); if (service.isSuccess) { comments.clear(); final List messages = service.result['comments']; for (var i = 0; i < messages.length; i++) { comments.add(MentionData.fromJson(messages[i])); } appState = AppState.idle; return; } appState = AppState.failed; } Future _getCommentsFromCommentsService() async { try { await AuthInterceptor.ensureFreshToken(); final token = TokenStorage.accessToken; Uri uri; if (contentUuid != null && contentUuid!.isNotEmpty) { final qp = { 'filter.contentId': '\$eq:$contentUuid', 'sortBy': 'createdAt:DESC', 'limit': '50', }; uri = Uri.parse('${AuthConfig.apiBaseUrl}/comments') .replace(queryParameters: qp); } else { uri = Uri.parse( '${AuthConfig.apiBaseUrl}/comments/me/mentions?limit=50', ); } final res = await http.get(uri, headers: { 'Accept': 'application/json', if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token', }).timeout(const Duration(seconds: 15)); if (res.statusCode != 200) { appState = AppState.failed; notifyListeners(); return; } final body = json.decode(res.body); final List dataRaw = body is Map && body['data'] is List ? body['data'] as List : (body is List ? body : const []); comments.clear(); for (final c in dataRaw.whereType()) { final map = Map.from(c); comments.add(_mentionFromCommentJson(map)); } appState = AppState.idle; notifyListeners(); } catch (e) { dev.log('getComments(new) error: $e', name: 'MentionsState'); appState = AppState.failed; notifyListeners(); } } MentionData _mentionFromCommentJson(Map json) { final user = json['user'] is Map ? Map.from(json['user'] as Map) : {}; final fullName = [ user['firstName']?.toString() ?? '', user['lastName']?.toString() ?? '', ].where((s) => s.isNotEmpty).join(' ').trim(); final mentionsRaw = json['mentions']; final mentions = mentionsRaw is List ? mentionsRaw.map((e) => e.toString()).toList() : []; return MentionData( id: (json['id']?.toString() ?? '').hashCode, text: (json['body'] ?? '').toString(), createdAt: (json['createdAt'] ?? DateTime.now().toIso8601String()) .toString(), mentions: mentions, user: UserOverview( id: (json['userId']?.toString() ?? '').hashCode, fullName: fullName.isEmpty ? 'کاربر' : fullName, photo: user['avatar']?.toString(), ), ); } Future getUsersMention(int id) async { if (!AuthConfig.useLegacyApi) { await _searchUsersFromAuth(id); return; } final service = RequestService( RequestHelper.usersMentions(searchUsers.text), ); await service.httpGet(); if (service.isSuccess) { users.clear(); final List resUsers = service.data('users'); users.addAll(mentionedUsers); for (var i = 0; i < resUsers.length; i++) { final user = UsersMention.fromJson(resUsers[i]); if (!mentionedUsers.map((e) => e.id).contains(user.id) && user.id != id) { users.add(user); } } appState = AppState.idle; return; } appState = AppState.failed; } Future _searchUsersFromAuth(int currentUserHashId) async { try { await AuthInterceptor.ensureFreshToken(); final token = TokenStorage.accessToken; final q = searchUsers.text.trim(); final params = { 'limit': '20', if (q.isNotEmpty) 'search': q, }; final uri = Uri.parse('${AuthConfig.apiBaseUrl}/comments/users/search') .replace(queryParameters: params); final res = await http.get(uri, headers: { 'Accept': 'application/json', if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token', }).timeout(const Duration(seconds: 15)); if (res.statusCode != 200) { appState = AppState.failed; notifyListeners(); return; } final body = json.decode(res.body); final List dataRaw = body is Map && body['data'] is List ? body['data'] as List : (body is List ? body : const []); users.clear(); // اول کاربران mention‌شده‌ی فعلی. users.addAll(mentionedUsers); for (final u in dataRaw.whereType()) { final map = Map.from(u); final uuid = map['id']?.toString() ?? ''; if (uuid.isEmpty) continue; final int intId = uuid.hashCode; _userUuidById[intId] = uuid; final fullName = [ map['firstName']?.toString() ?? '', map['lastName']?.toString() ?? '', ].where((s) => s.isNotEmpty).join(' ').trim(); final candidate = UsersMention( id: intId, name: fullName.isEmpty ? (map['username']?.toString() ?? '') : fullName, type: null, photo: map['avatar']?.toString(), ); if (mentionedUsers.map((e) => e.id).contains(candidate.id)) continue; if (candidate.id == currentUserHashId) continue; users.add(candidate); } appState = AppState.idle; notifyListeners(); } catch (e) { dev.log('searchUsers(new) error: $e', name: 'MentionsState'); appState = AppState.failed; notifyListeners(); } } Future addMention() async { if (!AuthConfig.useLegacyApi) { await _addMentionViaCommentsService(); return; } late List cList = comments; final user = DesignConfig.context!.read().user; cList.insert( 0, MentionData( id: 0, user: UserOverview( id: user.id, fullName: user.fullName, photo: user.photo, ), text: textFieldController.text, createdAt: DateTime.now().toString(), mentions: mentionedUsers.map((user) => user.name).toList(), ), ); final body = {}; body.addAll({ 'text': textFieldController.text, "mentions": mentionedUsers.map((user) => user.id).toList(), }); final service = RequestService( RequestHelper.mention(itemId, type), body: body, ); await service.post(); if (service.isSuccess) { cList.firstWhere((comment) => comment.id == 0).id = service.result['comment']['id']; mentionedUsers.clear(); users.clear(); searchUsers.text = ''; textFieldController.text = ''; showUsersForMentionsLayout = false; update(); } } Future _addMentionViaCommentsService() async { if (contentUuid == null || contentUuid!.isEmpty) { // بدون contentUuid، نمی‌توانیم کامنت در content-service ایجاد کنیم. return; } final text = textFieldController.text; if (text.trim().isEmpty) return; final mentionUuids = mentionedUsers .map((u) => _userUuidById[u.id]) .whereType() .toList(); try { await AuthInterceptor.ensureFreshToken(); final token = TokenStorage.accessToken; final uri = Uri.parse('${AuthConfig.apiBaseUrl}/comments'); final res = await http .post(uri, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', if (token != null && token.isNotEmpty) 'Authorization': 'Bearer $token', }, body: json.encode({ 'contentId': contentUuid, 'body': text, 'mentions': mentionUuids, })) .timeout(const Duration(seconds: 15)); if (res.statusCode < 200 || res.statusCode >= 300) { dev.log('addMention(new) failed status=${res.statusCode}', name: 'MentionsState'); return; } mentionedUsers.clear(); users.clear(); searchUsers.text = ''; textFieldController.text = ''; showUsersForMentionsLayout = false; await _getCommentsFromCommentsService(); update(); } catch (e) { dev.log('addMention(new) error: $e', name: 'MentionsState'); } } void deleteMention(int id) async { if (!AuthConfig.useLegacyApi) { // پاک‌کردن از سمت کاربر فعلی پشتیبانی نشده در API جدید. comments.removeWhere((e) => e.id == id); notifyListeners(); return; } final service = RequestService(RequestHelper.deleteComment(id)); await service.delete(); if (service.isSuccess) { comments.removeWhere((element) => element.id == id); } notifyListeners(); } }