Chepuhagram/lib/logic/contact_provider.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();
}
}