354 lines
11 KiB
Dart
354 lines
11 KiB
Dart
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<UsersMention> mentionedUsers = [];
|
||
int? commentId;
|
||
UserOverview? replyingTo;
|
||
bool showReplyBox = false;
|
||
bool showUsersForMentionsLayout = false;
|
||
bool showPrivates = false;
|
||
bool hideMentionedUser = false;
|
||
late String type;
|
||
|
||
final List<MentionData> comments = [];
|
||
final List<UsersMention> users = [];
|
||
|
||
int itemId = 0;
|
||
// UUID محتوای جدید (در صورت وجود) — برای فراخوانی content-service.
|
||
String? contentUuid;
|
||
// نگاشت UsersMention.id (hashCode) → keycloak UUID. هنگام ارسال
|
||
// mentionها، از این نگاشت استفاده میکنیم تا UUID واقعی فرستاده شود.
|
||
final Map<int, String> _userUuidById = {};
|
||
|
||
Future<void> 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<dynamic> 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<void> _getCommentsFromCommentsService() async {
|
||
try {
|
||
await AuthInterceptor.ensureFreshToken();
|
||
final token = TokenStorage.accessToken;
|
||
Uri uri;
|
||
if (contentUuid != null && contentUuid!.isNotEmpty) {
|
||
final qp = <String, String>{
|
||
'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<Map>()) {
|
||
final map = Map<String, dynamic>.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<String, dynamic> json) {
|
||
final user = json['user'] is Map
|
||
? Map<String, dynamic>.from(json['user'] as Map)
|
||
: <String, dynamic>{};
|
||
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()
|
||
: <String>[];
|
||
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<void> 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<dynamic> 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<void> _searchUsersFromAuth(int currentUserHashId) async {
|
||
try {
|
||
await AuthInterceptor.ensureFreshToken();
|
||
final token = TokenStorage.accessToken;
|
||
final q = searchUsers.text.trim();
|
||
final params = <String, String>{
|
||
'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<Map>()) {
|
||
final map = Map<String, dynamic>.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<void> addMention() async {
|
||
if (!AuthConfig.useLegacyApi) {
|
||
await _addMentionViaCommentsService();
|
||
return;
|
||
}
|
||
|
||
late List<MentionData> cList = comments;
|
||
final user = DesignConfig.context!.read<UserProvider>().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<void> _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<String>()
|
||
.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();
|
||
}
|
||
}
|