173 lines
5.8 KiB
Dart
173 lines
5.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '/data/models/contact_model.dart';
|
|
import '/data/repositories/contact_repository.dart';
|
|
import '/data/datasources/local_db_service.dart';
|
|
import '/domain/services/crypto_service.dart';
|
|
|
|
class ContactProvider extends ChangeNotifier {
|
|
final ContactRepository _repository = ContactRepository();
|
|
final LocalDbService _localDbService = LocalDbService();
|
|
final CryptoService _cryptoService = CryptoService();
|
|
List<Contact> _contacts = [];
|
|
List<Contact> _allContacts = [];
|
|
bool _isLoading = false;
|
|
bool _isFirstLoad = true;
|
|
String? _error;
|
|
int? _currentUserId;
|
|
|
|
List<Contact> get contacts => _contacts;
|
|
List<Contact> get allContacts => _allContacts;
|
|
bool get isLoading => _isLoading;
|
|
String? get error => _error;
|
|
|
|
void setCurrentUserId(int? id) {
|
|
_currentUserId = id;
|
|
notifyListeners();
|
|
}
|
|
|
|
int? getCurrentUserId() {
|
|
return _currentUserId;
|
|
}
|
|
|
|
Future<void> loadContacts() async {
|
|
if (_isFirstLoad) {
|
|
_isFirstLoad = false;
|
|
_isLoading = true;
|
|
}
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final allContacts = await _repository.fetchChatContacts();
|
|
// Фильтруем: исключаем себя (для основного списка - только чаты)
|
|
_contacts = allContacts
|
|
.where((contact) => contact.id != _currentUserId)
|
|
.toList();
|
|
_allContacts = _contacts;
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
|
|
// Обогащаем превью последним сообщением из локальной БД, не блокируя UI.
|
|
_enrichContactsWithLastMessages();
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
} finally {
|
|
// Если ошибка — выходим из состояния загрузки тут.
|
|
// Если всё ок — `_isLoading` уже сброшен выше, чтобы показать список быстрее.
|
|
if (_error != null) {
|
|
_isLoading = false;
|
|
}
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
// Метод для получения всех контактов (исключая себя) для нового чата
|
|
Future<void> loadAllContactsForNewChat() async {
|
|
_isLoading = true;
|
|
_error = null;
|
|
notifyListeners();
|
|
|
|
try {
|
|
final allContacts = await _repository.fetchAllUsers();
|
|
// Фильтруем только исключение самого себя
|
|
_allContacts = allContacts
|
|
.where((contact) => contact.id != _currentUserId)
|
|
.toList();
|
|
} catch (e) {
|
|
_error = e.toString();
|
|
} finally {
|
|
_isLoading = false;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<void> _enrichContactsWithLastMessages() async {
|
|
final myId = _currentUserId;
|
|
if (myId == null) return;
|
|
|
|
final myPrivKey = await _cryptoService.getPrivateKey();
|
|
|
|
final List<Contact> updated = List<Contact>.from(_contacts);
|
|
|
|
for (int i = 0; i < updated.length; i++) {
|
|
final contact = updated[i];
|
|
|
|
// 1) Если сервер уже прислал lastMessage — попробуем расшифровать превью.
|
|
if (contact.lastMessage != null &&
|
|
contact.lastMessage!.isNotEmpty &&
|
|
myPrivKey != null &&
|
|
contact.publicKey != null) {
|
|
try {
|
|
final sharedSecret = await _cryptoService.deriveSharedSecret(
|
|
myPrivKey,
|
|
contact.publicKey!,
|
|
);
|
|
final decrypted = await _cryptoService.decryptMessage(
|
|
contact.lastMessage!,
|
|
sharedSecret,
|
|
);
|
|
updated[i] = contact.copyWith(lastMessage: decrypted);
|
|
} catch (_) {
|
|
// Если расшифровать не удалось — оставляем как есть, дальше попробуем локальную БД.
|
|
}
|
|
}
|
|
|
|
// Если сервер уже отдал и сообщение, и время — не трогаем (контакты уже обогащены).
|
|
final contactAfterServer = updated[i];
|
|
if (contactAfterServer.lastMessage != null &&
|
|
contactAfterServer.lastMessage!.isNotEmpty &&
|
|
contactAfterServer.publicKey == null) {
|
|
// Чтобы не показывать в списке контактов "ciphertext", если ключа нет.
|
|
updated[i] = contactAfterServer.copyWith(
|
|
lastMessage: 'Новое сообщение',
|
|
);
|
|
}
|
|
|
|
final contactAfterServer2 = updated[i];
|
|
if (contactAfterServer2.lastMessage != null &&
|
|
contactAfterServer2.lastMessageTime != null) {
|
|
continue;
|
|
}
|
|
|
|
final last = await _localDbService.getLastMessage(contact.id, myId);
|
|
if (last == null) continue;
|
|
|
|
final rawContent = last['content']?.toString();
|
|
final rawTimestamp = last['timestamp']?.toString();
|
|
final lastTime = rawTimestamp == null
|
|
? null
|
|
: DateTime.tryParse(rawTimestamp);
|
|
|
|
String? preview;
|
|
if (rawContent != null && rawContent.isNotEmpty) {
|
|
// Пытаемся расшифровать превью, если есть ключи.
|
|
try {
|
|
if (myPrivKey != null && contact.publicKey != null) {
|
|
final sharedSecret = await _cryptoService.deriveSharedSecret(
|
|
myPrivKey,
|
|
contact.publicKey!,
|
|
);
|
|
preview = await _cryptoService.decryptMessage(
|
|
rawContent,
|
|
sharedSecret,
|
|
);
|
|
} else {
|
|
preview = 'Новое сообщение';
|
|
}
|
|
} catch (_) {
|
|
preview = 'Новое сообщение';
|
|
}
|
|
}
|
|
|
|
updated[i] = contactAfterServer2.copyWith(
|
|
lastMessage: preview,
|
|
lastMessageTime: contactAfterServer2.lastMessageTime ?? lastTime,
|
|
);
|
|
}
|
|
|
|
_contacts = updated;
|
|
_allContacts = updated;
|
|
notifyListeners();
|
|
}
|
|
}
|