Кэширование ID пользователей из чатов (на 24 часа)
This commit is contained in:
@@ -190,7 +190,6 @@ extension ApiServiceAuth on ApiService {
|
|||||||
authToken = currentAccount.token;
|
authToken = currentAccount.token;
|
||||||
userId = currentAccount.userId;
|
userId = currentAccount.userId;
|
||||||
|
|
||||||
_messageCache.clear();
|
|
||||||
_messageQueue.clear();
|
_messageQueue.clear();
|
||||||
_lastChatsPayload = null;
|
_lastChatsPayload = null;
|
||||||
_chatsFetchedInThisSession = false;
|
_chatsFetchedInThisSession = false;
|
||||||
@@ -257,4 +256,3 @@ extension ApiServiceAuth on ApiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,119 @@
|
|||||||
part of 'api_service.dart';
|
part of 'api_service.dart';
|
||||||
|
|
||||||
extension ApiServiceChats on ApiService {
|
extension ApiServiceChats on ApiService {
|
||||||
|
Future<void> _sendAuthRequestAfterHandshake() async {
|
||||||
|
if (authToken == null) {
|
||||||
|
print("Токен не найден, пропускаем автоматическую авторизацию");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_chatsFetchedInThisSession) {
|
||||||
|
print("Авторизация уже выполнена в этой сессии, пропускаем");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _ensureCacheServicesInitialized();
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final deviceId =
|
||||||
|
prefs.getString('spoof_deviceid') ?? generateRandomDeviceId();
|
||||||
|
|
||||||
|
if (prefs.getString('spoof_deviceid') == null) {
|
||||||
|
await prefs.setString('spoof_deviceid', deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final payload = {
|
||||||
|
"chatsCount": 100,
|
||||||
|
"chatsSync": 0,
|
||||||
|
"contactsSync": 0,
|
||||||
|
"draftsSync": 0,
|
||||||
|
"interactive": true,
|
||||||
|
"presenceSync": 0,
|
||||||
|
"token": authToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (userId != null) {
|
||||||
|
payload["userId"] = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Автоматически отправляем opcode 19 для авторизации...");
|
||||||
|
final int chatSeq = _sendMessage(19, payload);
|
||||||
|
final chatResponse = await messages.firstWhere(
|
||||||
|
(msg) => msg['seq'] == chatSeq,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chatResponse['cmd'] == 1) {
|
||||||
|
print("✅ Авторизация (opcode 19) успешна. Сессия ГОТОВА.");
|
||||||
|
_isSessionReady = true;
|
||||||
|
|
||||||
|
_connectionStatusController.add("ready");
|
||||||
|
_updateConnectionState(
|
||||||
|
conn_state.ConnectionState.ready,
|
||||||
|
message: 'Авторизация успешна',
|
||||||
|
);
|
||||||
|
|
||||||
|
final profile = chatResponse['payload']?['profile'];
|
||||||
|
final contactProfile = profile?['contact'];
|
||||||
|
|
||||||
|
if (contactProfile != null && contactProfile['id'] != null) {
|
||||||
|
print(
|
||||||
|
"[_sendAuthRequestAfterHandshake] ✅ Профиль и ID пользователя найдены. ID: ${contactProfile['id']}. ЗАПУСКАЕМ АНАЛИТИКУ.",
|
||||||
|
);
|
||||||
|
_userId = contactProfile['id'];
|
||||||
|
_sessionId = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
_lastActionTime = _sessionId;
|
||||||
|
|
||||||
|
sendNavEvent('COLD_START');
|
||||||
|
|
||||||
|
_sendInitialSetupRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_onlineCompleter != null && !_onlineCompleter!.isCompleted) {
|
||||||
|
_onlineCompleter!.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
final chatListJson = chatResponse['payload']?['chats'] ?? [];
|
||||||
|
final contactListJson = chatResponse['payload']?['contacts'] ?? [];
|
||||||
|
final presence = chatResponse['payload']?['presence'];
|
||||||
|
final config = chatResponse['payload']?['config'];
|
||||||
|
|
||||||
|
if (presence != null) {
|
||||||
|
updatePresenceData(presence);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config != null) {
|
||||||
|
_processServerPrivacyConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = {
|
||||||
|
'chats': chatListJson,
|
||||||
|
'contacts': contactListJson,
|
||||||
|
'profile': profile,
|
||||||
|
'presence': presence,
|
||||||
|
'config': config,
|
||||||
|
};
|
||||||
|
_lastChatsPayload = result;
|
||||||
|
|
||||||
|
final contacts = contactListJson
|
||||||
|
.map((json) => Contact.fromJson(json))
|
||||||
|
.toList();
|
||||||
|
updateContactCache(contacts);
|
||||||
|
_lastChatsAt = DateTime.now();
|
||||||
|
_preloadContactAvatars(contacts);
|
||||||
|
unawaited(
|
||||||
|
_chatCacheService.cacheChats(
|
||||||
|
chatListJson.cast<Map<String, dynamic>>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
unawaited(_chatCacheService.cacheContacts(contacts));
|
||||||
|
_chatsFetchedInThisSession = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Ошибка при автоматической авторизации: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void createGroup(String name, List<int> participantIds) {
|
void createGroup(String name, List<int> participantIds) {
|
||||||
final payload = {"name": name, "participantIds": participantIds};
|
final payload = {"name": name, "participantIds": participantIds};
|
||||||
_sendMessage(48, payload);
|
_sendMessage(48, payload);
|
||||||
@@ -357,22 +470,29 @@ extension ApiServiceChats on ApiService {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
final contactIds = <int>{};
|
List<dynamic> contactListJson =
|
||||||
for (var chatJson in chatListJson) {
|
chatResponse['payload']?['contacts'] ?? [];
|
||||||
final participants = chatJson['participants'] as Map<String, dynamic>;
|
|
||||||
contactIds.addAll(participants.keys.map((id) => int.parse(id)));
|
if (contactListJson.isEmpty) {
|
||||||
|
final contactIds = <int>{};
|
||||||
|
for (var chatJson in chatListJson) {
|
||||||
|
final participants =
|
||||||
|
chatJson['participants'] as Map<String, dynamic>? ?? {};
|
||||||
|
contactIds.addAll(participants.keys.map((id) => int.parse(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contactIds.isNotEmpty) {
|
||||||
|
final int contactSeq = _sendMessage(32, {
|
||||||
|
"contactIds": contactIds.toList(),
|
||||||
|
});
|
||||||
|
final contactResponse = await messages.firstWhere(
|
||||||
|
(msg) => msg['seq'] == contactSeq,
|
||||||
|
);
|
||||||
|
|
||||||
|
contactListJson = contactResponse['payload']?['contacts'] ?? [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final int contactSeq = _sendMessage(32, {
|
|
||||||
"contactIds": contactIds.toList(),
|
|
||||||
});
|
|
||||||
final contactResponse = await messages.firstWhere(
|
|
||||||
(msg) => msg['seq'] == contactSeq,
|
|
||||||
);
|
|
||||||
|
|
||||||
final List<dynamic> contactListJson =
|
|
||||||
contactResponse['payload']?['contacts'] ?? [];
|
|
||||||
|
|
||||||
if (presence != null) {
|
if (presence != null) {
|
||||||
updatePresenceData(presence);
|
updatePresenceData(presence);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -363,11 +363,15 @@ extension ApiServiceConnection on ApiService {
|
|||||||
);
|
);
|
||||||
_startHealthMonitoring();
|
_startHealthMonitoring();
|
||||||
|
|
||||||
if (_onlineCompleter != null && !_onlineCompleter!.isCompleted) {
|
|
||||||
_onlineCompleter!.complete();
|
|
||||||
}
|
|
||||||
_startPinging();
|
_startPinging();
|
||||||
_processMessageQueue();
|
_processMessageQueue();
|
||||||
|
|
||||||
|
if (authToken != null && !_chatsFetchedInThisSession) {
|
||||||
|
print(
|
||||||
|
"Токен найден, автоматически запускаем авторизацию (opcode 19)...",
|
||||||
|
);
|
||||||
|
unawaited(_sendAuthRequestAfterHandshake());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decodedMessage is Map && decodedMessage['cmd'] == 3) {
|
if (decodedMessage is Map && decodedMessage['cmd'] == 3) {
|
||||||
@@ -670,8 +674,6 @@ extension ApiServiceConnection on ApiService {
|
|||||||
_onlineCompleter = Completer<void>();
|
_onlineCompleter = Completer<void>();
|
||||||
_chatsFetchedInThisSession = false;
|
_chatsFetchedInThisSession = false;
|
||||||
|
|
||||||
clearAllCaches();
|
|
||||||
|
|
||||||
_currentUrlIndex = 0;
|
_currentUrlIndex = 0;
|
||||||
|
|
||||||
_reconnectDelaySeconds = (_reconnectDelaySeconds * 2).clamp(1, 30);
|
_reconnectDelaySeconds = (_reconnectDelaySeconds * 2).clamp(1, 30);
|
||||||
@@ -721,7 +723,6 @@ extension ApiServiceConnection on ApiService {
|
|||||||
_currentUrlIndex = 0;
|
_currentUrlIndex = 0;
|
||||||
_onlineCompleter = Completer<void>();
|
_onlineCompleter = Completer<void>();
|
||||||
|
|
||||||
clearAllCaches();
|
|
||||||
_messageQueue.clear();
|
_messageQueue.clear();
|
||||||
_presenceData.clear();
|
_presenceData.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,20 @@ extension ApiServiceContacts on ApiService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_isSessionOnline || !_isSessionReady) {
|
||||||
|
print(
|
||||||
|
'ApiService: сессия еще не готова для запроса заблокированных контактов, ждем...',
|
||||||
|
);
|
||||||
|
await waitUntilOnline();
|
||||||
|
|
||||||
|
if (!_isSessionReady) {
|
||||||
|
print(
|
||||||
|
'ApiService: сессия все еще не готова после ожидания, отменяем запрос',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_isLoadingBlockedContacts = true;
|
_isLoadingBlockedContacts = true;
|
||||||
print('ApiService: запрашиваем заблокированные контакты');
|
print('ApiService: запрашиваем заблокированные контакты');
|
||||||
_sendMessage(36, {'status': 'BLOCKED', 'count': 100, 'from': 0});
|
_sendMessage(36, {'status': 'BLOCKED', 'count': 100, 'from': 0});
|
||||||
@@ -278,12 +292,23 @@ extension ApiServiceContacts on ApiService {
|
|||||||
|
|
||||||
Future<List<Contact>> fetchContactsByIds(List<int> contactIds) async {
|
Future<List<Contact>> fetchContactsByIds(List<int> contactIds) async {
|
||||||
if (contactIds.isEmpty) {
|
if (contactIds.isEmpty) {
|
||||||
|
print(
|
||||||
|
'⚠️ [fetchContactsByIds] Пустой список contactIds - пропускаем запрос',
|
||||||
|
);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
print('Запрашиваем данные для ${contactIds.length} контактов...');
|
print(
|
||||||
|
'📡 [fetchContactsByIds] Запрашиваем данные для ${contactIds.length} контактов...',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'📡 [fetchContactsByIds] IDs: ${contactIds.take(10).join(', ')}${contactIds.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
final int contactSeq = _sendMessage(32, {"contactIds": contactIds});
|
final int contactSeq = _sendMessage(32, {"contactIds": contactIds});
|
||||||
|
print(
|
||||||
|
'📤 [fetchContactsByIds] Отправлен опкод 32 с seq=$contactSeq и ${contactIds.length} ID',
|
||||||
|
);
|
||||||
|
|
||||||
final contactResponse = await messages
|
final contactResponse = await messages
|
||||||
.firstWhere((msg) => msg['seq'] == contactSeq)
|
.firstWhere((msg) => msg['seq'] == contactSeq)
|
||||||
@@ -291,7 +316,7 @@ extension ApiServiceContacts on ApiService {
|
|||||||
|
|
||||||
if (contactResponse['cmd'] == 3) {
|
if (contactResponse['cmd'] == 3) {
|
||||||
print(
|
print(
|
||||||
"Ошибка при получении контактов по ID: ${contactResponse['payload']}",
|
"❌ [fetchContactsByIds] Ошибка при получении контактов: ${contactResponse['payload']}",
|
||||||
);
|
);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -302,13 +327,29 @@ extension ApiServiceContacts on ApiService {
|
|||||||
.map((json) => Contact.fromJson(json))
|
.map((json) => Contact.fromJson(json))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
print(
|
||||||
|
'📦 [fetchContactsByIds] Получено ${contacts.length} контактов из ${contactIds.length} запрошенных',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (contacts.length < contactIds.length) {
|
||||||
|
final receivedIds = contacts.map((c) => c.id).toSet();
|
||||||
|
final missingIds = contactIds
|
||||||
|
.where((id) => !receivedIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
print(
|
||||||
|
'⚠️ [fetchContactsByIds] Отсутствуют ${missingIds.length} контактов: ${missingIds.take(5).join(', ')}${missingIds.length > 5 ? '...' : ''}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (final contact in contacts) {
|
for (final contact in contacts) {
|
||||||
_contactCache[contact.id] = contact;
|
_contactCache[contact.id] = contact;
|
||||||
}
|
}
|
||||||
print("Получены и закэшированы данные для ${contacts.length} контактов.");
|
print(
|
||||||
|
"✅ [fetchContactsByIds] Закэшированы данные для ${contacts.length} контактов",
|
||||||
|
);
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Исключение при получении контактов по ID: $e');
|
print('❌ [fetchContactsByIds] Исключение при получении контактов: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,13 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (contacts.isNotEmpty && mounted) {
|
if (contacts.isNotEmpty && mounted) {
|
||||||
final contact = contacts.first;
|
final contact = contacts.first;
|
||||||
_contactDetailsCache[contact.id] = contact;
|
_contactDetailsCache[contact.id] = contact;
|
||||||
|
|
||||||
|
final allChatContacts = _contactDetailsCache.values.toList();
|
||||||
|
await ChatCacheService().cacheChatContacts(
|
||||||
|
widget.chatId,
|
||||||
|
allChatContacts,
|
||||||
|
);
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -173,6 +180,152 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _loadGroupParticipants() async {
|
||||||
|
try {
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] Начинаем загрузку участников группы...',
|
||||||
|
);
|
||||||
|
|
||||||
|
final chatData = ApiService.instance.lastChatsPayload;
|
||||||
|
if (chatData == null) {
|
||||||
|
print('❌ [_loadGroupParticipants] chatData == null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final chats = chatData['chats'] as List<dynamic>?;
|
||||||
|
if (chats == null) {
|
||||||
|
print('❌ [_loadGroupParticipants] chats == null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] Ищем чат с ID ${widget.chatId} среди ${chats.length} чатов...',
|
||||||
|
);
|
||||||
|
|
||||||
|
final currentChat = chats.firstWhere(
|
||||||
|
(chat) => chat['id'] == widget.chatId,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentChat == null) {
|
||||||
|
print('❌ [_loadGroupParticipants] Чат с ID ${widget.chatId} не найден');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'✅ [_loadGroupParticipants] Чат найден: ${currentChat['title'] ?? 'Без названия'}',
|
||||||
|
);
|
||||||
|
|
||||||
|
final participants = currentChat['participants'] as Map<String, dynamic>?;
|
||||||
|
if (participants == null || participants.isEmpty) {
|
||||||
|
print('❌ [_loadGroupParticipants] Список участников пуст');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] Найдено ${participants.length} участников в чате',
|
||||||
|
);
|
||||||
|
|
||||||
|
final participantIds = participants.keys
|
||||||
|
.map((id) => int.tryParse(id))
|
||||||
|
.where((id) => id != null)
|
||||||
|
.cast<int>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (participantIds.isEmpty) {
|
||||||
|
print('❌ [_loadGroupParticipants] participantIds пуст после парсинга');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] Обрабатываем ${participantIds.length} ID участников...',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] IDs: ${participantIds.take(10).join(', ')}${participantIds.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
|
|
||||||
|
final idsToFetch = participantIds
|
||||||
|
.where((id) => !_contactDetailsCache.containsKey(id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] В кэше уже есть: ${participantIds.length - idsToFetch.length} контактов',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'🔍 [_loadGroupParticipants] Нужно загрузить: ${idsToFetch.length} контактов',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (idsToFetch.isEmpty) {
|
||||||
|
print('✅ [_loadGroupParticipants] Все участники уже в кэше');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(
|
||||||
|
'📡 [_loadGroupParticipants] Загружаем информацию о ${idsToFetch.length} участниках...',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'📡 [_loadGroupParticipants] IDs для загрузки: ${idsToFetch.take(10).join(', ')}${idsToFetch.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
|
|
||||||
|
final contacts = await ApiService.instance.fetchContactsByIds(idsToFetch);
|
||||||
|
|
||||||
|
print(
|
||||||
|
'📦 [_loadGroupParticipants] Получено ${contacts.length} контактов от API из ${idsToFetch.length} запрошенных',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (contacts.isNotEmpty) {
|
||||||
|
for (final contact in contacts) {
|
||||||
|
print(' 📇 Контакт: ${contact.name} (ID: ${contact.id})');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
for (final contact in contacts) {
|
||||||
|
_contactDetailsCache[contact.id] = contact;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await ChatCacheService().cacheChatContacts(widget.chatId, contacts);
|
||||||
|
|
||||||
|
print(
|
||||||
|
'✅ [_loadGroupParticipants] Загружено и сохранено ${contacts.length} контактов',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'✅ [_loadGroupParticipants] Всего в кэше теперь: ${_contactDetailsCache.length} контактов',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (contacts.length < idsToFetch.length) {
|
||||||
|
final receivedIds = contacts.map((c) => c.id).toSet();
|
||||||
|
final missingIds = idsToFetch
|
||||||
|
.where((id) => !receivedIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
print(
|
||||||
|
'⚠️ [_loadGroupParticipants] Не получены данные для ${missingIds.length} контактов из ${idsToFetch.length} запрошенных',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'⚠️ [_loadGroupParticipants] Отсутствующие ID: ${missingIds.take(10).join(', ')}${missingIds.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
'⚠️ [_loadGroupParticipants] Widget не mounted, контакты не сохранены',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('❌ [_loadGroupParticipants] API вернул ПУСТОЙ список контактов!');
|
||||||
|
print(
|
||||||
|
'❌ [_loadGroupParticipants] Было запрошено ${idsToFetch.length} ID',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'❌ [_loadGroupParticipants] Запрошенные ID: ${idsToFetch.take(10).join(', ')}${idsToFetch.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
print('❌ [_loadGroupParticipants] Ошибка загрузки участников группы: $e');
|
||||||
|
print('❌ [_loadGroupParticipants] StackTrace: $stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -185,6 +338,13 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
Future<void> _initializeChat() async {
|
Future<void> _initializeChat() async {
|
||||||
await _loadCachedContacts();
|
await _loadCachedContacts();
|
||||||
|
|
||||||
|
if (!widget.isGroupChat && !widget.isChannel) {
|
||||||
|
_contactDetailsCache[widget.contact.id] = widget.contact;
|
||||||
|
print(
|
||||||
|
'✅ [_initializeChat] Собеседник добавлен в кэш: ${widget.contact.name} (ID: ${widget.contact.id})',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final profileData = ApiService.instance.lastChatsPayload?['profile'];
|
final profileData = ApiService.instance.lastChatsPayload?['profile'];
|
||||||
final contactProfile = profileData?['contact'] as Map<String, dynamic>?;
|
final contactProfile = profileData?['contact'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
@@ -193,11 +353,34 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
contactProfile['id'] != 0) {
|
contactProfile['id'] != 0) {
|
||||||
_actualMyId = contactProfile['id'];
|
_actualMyId = contactProfile['id'];
|
||||||
print('✅ ID пользователя успешно получен из ApiService: $_actualMyId');
|
print('✅ ID пользователя успешно получен из ApiService: $_actualMyId');
|
||||||
|
|
||||||
|
try {
|
||||||
|
final myContact = Contact.fromJson(contactProfile);
|
||||||
|
_contactDetailsCache[_actualMyId!] = myContact;
|
||||||
|
print(
|
||||||
|
'✅ [_initializeChat] Собственный профиль добавлен в кэш: ${myContact.name} (ID: $_actualMyId)',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(
|
||||||
|
'⚠️ [_initializeChat] Не удалось добавить собственный профиль в кэш: $e',
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_actualMyId = widget.myId;
|
_actualMyId = widget.myId;
|
||||||
print('ПРЕДУПРЕЖДЕНИЕ: Используется ID из виджета: $_actualMyId');
|
print('ПРЕДУПРЕЖДЕНИЕ: Используется ID из виджета: $_actualMyId');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!widget.isGroupChat && !widget.isChannel) {
|
||||||
|
final contactsToCache = _contactDetailsCache.values.toList();
|
||||||
|
await ChatCacheService().cacheChatContacts(
|
||||||
|
widget.chatId,
|
||||||
|
contactsToCache,
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'✅ [_initializeChat] Сохранено ${contactsToCache.length} контактов в кэш чата (включая собственный профиль)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isIdReady = true;
|
_isIdReady = true;
|
||||||
@@ -395,6 +578,11 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_messages.clear();
|
_messages.clear();
|
||||||
_messages.addAll(cachedMessages);
|
_messages.addAll(cachedMessages);
|
||||||
|
|
||||||
|
if (widget.isGroupChat) {
|
||||||
|
await _loadGroupParticipants();
|
||||||
|
}
|
||||||
|
|
||||||
_buildChatItems();
|
_buildChatItems();
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingHistory = false;
|
_isLoadingHistory = false;
|
||||||
@@ -427,7 +615,17 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
final idsToFetch = senderIds
|
final idsToFetch = senderIds
|
||||||
.where((id) => !_contactDetailsCache.containsKey(id))
|
.where((id) => !_contactDetailsCache.containsKey(id))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (idsToFetch.isNotEmpty) {
|
if (idsToFetch.isNotEmpty) {
|
||||||
|
print(
|
||||||
|
'📡 [_paginateInitialLoad] Загружаем ${idsToFetch.length} отсутствующих контактов из ${senderIds.length} отправителей...',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'📡 [_paginateInitialLoad] В кэше: ${senderIds.length - idsToFetch.length}, нужно загрузить: ${idsToFetch.length}',
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'📡 [_paginateInitialLoad] IDs для загрузки: ${idsToFetch.take(10).join(', ')}${idsToFetch.length > 10 ? '...' : ''}',
|
||||||
|
);
|
||||||
final newContacts = await ApiService.instance.fetchContactsByIds(
|
final newContacts = await ApiService.instance.fetchContactsByIds(
|
||||||
idsToFetch,
|
idsToFetch,
|
||||||
);
|
);
|
||||||
@@ -435,10 +633,29 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
for (final contact in newContacts) {
|
for (final contact in newContacts) {
|
||||||
_contactDetailsCache[contact.id] = contact;
|
_contactDetailsCache[contact.id] = contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newContacts.isNotEmpty) {
|
||||||
|
final allChatContacts = _contactDetailsCache.values.toList();
|
||||||
|
await ChatCacheService().cacheChatContacts(
|
||||||
|
widget.chatId,
|
||||||
|
allChatContacts,
|
||||||
|
);
|
||||||
|
print(
|
||||||
|
'✅ [_paginateInitialLoad] Обновлен кэш: ${allChatContacts.length} контактов для чата ${widget.chatId}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
'✅ [_paginateInitialLoad] Все ${senderIds.length} отправителей уже в кэше',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await chatCacheService.cacheChatMessages(widget.chatId, allMessages);
|
await chatCacheService.cacheChatMessages(widget.chatId, allMessages);
|
||||||
|
|
||||||
|
if (widget.isGroupChat) {
|
||||||
|
await _loadGroupParticipants();
|
||||||
|
}
|
||||||
|
|
||||||
final page = _anyOptimize ? _optPage : _pageSize;
|
final page = _anyOptimize ? _optPage : _pageSize;
|
||||||
final slice = allMessages.length > page
|
final slice = allMessages.length > page
|
||||||
? allMessages.sublist(allMessages.length - page)
|
? allMessages.sublist(allMessages.length - page)
|
||||||
@@ -1587,14 +1804,30 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadCachedContacts() async {
|
Future<void> _loadCachedContacts() async {
|
||||||
|
final chatContacts = await ChatCacheService().getCachedChatContacts(
|
||||||
|
widget.chatId,
|
||||||
|
);
|
||||||
|
if (chatContacts != null && chatContacts.isNotEmpty) {
|
||||||
|
for (final contact in chatContacts) {
|
||||||
|
_contactDetailsCache[contact.id] = contact;
|
||||||
|
}
|
||||||
|
print(
|
||||||
|
'✅ Загружено ${_contactDetailsCache.length} контактов из кэша чата ${widget.chatId}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если нет кэша чата, загружаем глобальный кэш
|
||||||
final cachedContacts = await ChatCacheService().getCachedContacts();
|
final cachedContacts = await ChatCacheService().getCachedContacts();
|
||||||
if (cachedContacts != null && cachedContacts.isNotEmpty) {
|
if (cachedContacts != null && cachedContacts.isNotEmpty) {
|
||||||
for (final contact in cachedContacts) {
|
for (final contact in cachedContacts) {
|
||||||
_contactDetailsCache[contact.id] = contact;
|
_contactDetailsCache[contact.id] = contact;
|
||||||
}
|
}
|
||||||
print(
|
print(
|
||||||
'✅ Кэш контактов для экрана чата заполнен из ChatCacheService: ${_contactDetailsCache.length} контактов.',
|
'✅ Загружено ${_contactDetailsCache.length} контактов из глобального кэша',
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
print('⚠️ Кэш контактов пуст, будет загружено с сервера');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1746,9 +1979,12 @@ class _ChatScreenState extends State<ChatScreen> {
|
|||||||
if (shouldShowName) {
|
if (shouldShowName) {
|
||||||
final senderContact =
|
final senderContact =
|
||||||
_contactDetailsCache[message.senderId];
|
_contactDetailsCache[message.senderId];
|
||||||
senderName =
|
if (senderContact != null) {
|
||||||
senderContact?.name ??
|
senderName = senderContact.name;
|
||||||
'Участник ${message.senderId}';
|
} else {
|
||||||
|
senderName = 'ID ${message.senderId}';
|
||||||
|
_loadContactIfNeeded(message.senderId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final hasPhoto = item.message.attaches.any(
|
final hasPhoto = item.message.attaches.any(
|
||||||
@@ -4144,7 +4380,8 @@ class _AddMemberDialogState extends State<_AddMemberDialog> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final contact = widget.contacts[index];
|
final contact = widget.contacts[index];
|
||||||
final contactId = contact['id'] as int;
|
final contactId = contact['id'] as int;
|
||||||
final contactName = contact['names']?[0]?['name'] ?? 'Неизвестный';
|
final contactName =
|
||||||
|
contact['names']?[0]?['name'] ?? 'ID $contactId';
|
||||||
final isSelected = _selectedContacts.contains(contactId);
|
final isSelected = _selectedContacts.contains(contactId);
|
||||||
|
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
@@ -4316,7 +4553,8 @@ class _ControlMessageChip extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final eventType = controlAttach['event'];
|
final eventType = controlAttach['event'];
|
||||||
final senderName = contacts[message.senderId]?.name ?? 'Неизвестный';
|
final senderName =
|
||||||
|
contacts[message.senderId]?.name ?? 'ID ${message.senderId}';
|
||||||
final isMe = message.senderId == myId;
|
final isMe = message.senderId == myId;
|
||||||
final senderDisplayName = isMe ? 'Вы' : senderName;
|
final senderDisplayName = isMe ? 'Вы' : senderName;
|
||||||
|
|
||||||
@@ -4506,18 +4744,41 @@ class _ControlMessageChip extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void openUserProfileById(BuildContext context, int userId) {
|
Future<void> openUserProfileById(BuildContext context, int userId) async {
|
||||||
final contact = ApiService.instance.getCachedContact(userId);
|
var contact = ApiService.instance.getCachedContact(userId);
|
||||||
|
|
||||||
|
if (contact == null) {
|
||||||
|
print(
|
||||||
|
'⚠️ [openUserProfileById] Контакт $userId не найден в кэше, загружаем с сервера...',
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final contacts = await ApiService.instance.fetchContactsByIds([userId]);
|
||||||
|
if (contacts.isNotEmpty) {
|
||||||
|
contact = contacts.first;
|
||||||
|
print(
|
||||||
|
'✅ [openUserProfileById] Контакт $userId загружен: ${contact.name}',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
print(
|
||||||
|
'❌ [openUserProfileById] Сервер не вернул данные для контакта $userId',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ [openUserProfileById] Ошибка загрузки контакта $userId: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
final isGroup = contact.id < 0; // Groups have negative IDs
|
final contactData = contact;
|
||||||
|
final isGroup = contactData.id < 0;
|
||||||
|
|
||||||
if (isGroup) {
|
if (isGroup) {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) => GroupProfileDraggableDialog(contact: contact),
|
builder: (context) => GroupProfileDraggableDialog(contact: contactData),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
@@ -4525,7 +4786,7 @@ void openUserProfileById(BuildContext context, int userId) {
|
|||||||
opaque: false,
|
opaque: false,
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
pageBuilder: (context, animation, secondaryAnimation) {
|
pageBuilder: (context, animation, secondaryAnimation) {
|
||||||
return ContactProfileDialog(contact: contact);
|
return ContactProfileDialog(contact: contactData);
|
||||||
},
|
},
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
return FadeTransition(opacity: animation, child: child);
|
return FadeTransition(opacity: animation, child: child);
|
||||||
@@ -4538,12 +4799,12 @@ void openUserProfileById(BuildContext context, int userId) {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: Text('Профиль пользователя $userId'),
|
title: const Text('Ошибка'),
|
||||||
content: Text('Информация о пользователе не найдена в кэше'),
|
content: Text('Не удалось загрузить информацию о пользователе $userId'),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
child: Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
List<SearchResult> _searchResults = [];
|
List<SearchResult> _searchResults = [];
|
||||||
String _searchFilter = 'all';
|
String _searchFilter = 'all';
|
||||||
bool _hasRequestedBlockedContacts = false;
|
bool _hasRequestedBlockedContacts = false;
|
||||||
|
final Set<int> _loadingContactIds = {};
|
||||||
|
|
||||||
List<ChatFolder> _folders = [];
|
List<ChatFolder> _folders = [];
|
||||||
String? _selectedFolderId;
|
String? _selectedFolderId;
|
||||||
@@ -1503,6 +1504,30 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _loadMissingContact(int contactId) async {
|
||||||
|
if (_loadingContactIds.contains(contactId) ||
|
||||||
|
_contacts.containsKey(contactId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadingContactIds.add(contactId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final contacts = await ApiService.instance.fetchContactsByIds([
|
||||||
|
contactId,
|
||||||
|
]);
|
||||||
|
if (contacts.isNotEmpty && mounted) {
|
||||||
|
setState(() {
|
||||||
|
_contacts[contactId] = contacts.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Ошибка загрузки контакта $contactId: $e');
|
||||||
|
} finally {
|
||||||
|
_loadingContactIds.remove(contactId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _formatTimestamp(int timestamp) {
|
String _formatTimestamp(int timestamp) {
|
||||||
final dt = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
final dt = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
@@ -2343,12 +2368,13 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
final bool isSavedMessages = _isSavedMessages(chat);
|
final bool isSavedMessages = _isSavedMessages(chat);
|
||||||
|
|
||||||
final Contact? contact;
|
final Contact? contact;
|
||||||
|
int? otherParticipantId;
|
||||||
if (isSavedMessages) {
|
if (isSavedMessages) {
|
||||||
contact = _contacts[chat.ownerId];
|
contact = _contacts[chat.ownerId];
|
||||||
} else if (isGroupChat) {
|
} else if (isGroupChat) {
|
||||||
contact = null;
|
contact = null;
|
||||||
} else {
|
} else {
|
||||||
final otherParticipantId = chat.participantIds.firstWhere(
|
otherParticipantId = chat.participantIds.firstWhere(
|
||||||
(id) => id != chat.ownerId,
|
(id) => id != chat.ownerId,
|
||||||
orElse: () => 0,
|
orElse: () => 0,
|
||||||
);
|
);
|
||||||
@@ -2360,11 +2386,23 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final bool isChannel = chat.type == 'CHANNEL';
|
final bool isChannel = chat.type == 'CHANNEL';
|
||||||
final String title = isGroupChat
|
String title;
|
||||||
? (chat.title?.isNotEmpty == true ? chat.title! : "Группа")
|
if (isGroupChat) {
|
||||||
: (isSavedMessages
|
title = chat.title?.isNotEmpty == true
|
||||||
? "Избранное"
|
? chat.title!
|
||||||
: contact?.name ?? "Unknown");
|
: "Группа";
|
||||||
|
} else if (isSavedMessages) {
|
||||||
|
title = "Избранное";
|
||||||
|
} else if (contact != null) {
|
||||||
|
title = contact.name;
|
||||||
|
} else if (chat.title?.isNotEmpty == true) {
|
||||||
|
title = chat.title!;
|
||||||
|
} else {
|
||||||
|
title = "ID ${otherParticipantId ?? 0}";
|
||||||
|
if (otherParticipantId != null && otherParticipantId != 0) {
|
||||||
|
_loadMissingContact(otherParticipantId);
|
||||||
|
}
|
||||||
|
}
|
||||||
final String? avatarUrl = isGroupChat
|
final String? avatarUrl = isGroupChat
|
||||||
? chat.baseIconUrl
|
? chat.baseIconUrl
|
||||||
: (isSavedMessages ? null : contact?.photoBaseUrl);
|
: (isSavedMessages ? null : contact?.photoBaseUrl);
|
||||||
@@ -2512,7 +2550,12 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
: Text(
|
: Text(
|
||||||
isSavedMessages
|
isSavedMessages
|
||||||
? "Избранное"
|
? "Избранное"
|
||||||
: (contact?.name ?? 'Unknown'),
|
: (contact?.name ??
|
||||||
|
(chat.title?.isNotEmpty == true
|
||||||
|
? chat.title!
|
||||||
|
: (otherParticipantId != null
|
||||||
|
? 'ID $otherParticipantId'
|
||||||
|
: 'ID 0'))),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: colors.onSurface,
|
color: colors.onSurface,
|
||||||
@@ -3779,7 +3822,14 @@ class _ChatsScreenState extends State<ChatsScreen>
|
|||||||
);
|
);
|
||||||
contact = _contacts[otherParticipantId];
|
contact = _contacts[otherParticipantId];
|
||||||
|
|
||||||
title = contact?.name ?? "Неизвестный чат";
|
if (contact != null) {
|
||||||
|
title = contact.name;
|
||||||
|
} else if (chat.title?.isNotEmpty == true) {
|
||||||
|
title = chat.title!;
|
||||||
|
} else {
|
||||||
|
title = "ID $otherParticipantId";
|
||||||
|
_loadMissingContact(otherParticipantId);
|
||||||
|
}
|
||||||
avatarUrl = contact?.photoBaseUrl;
|
avatarUrl = contact?.photoBaseUrl;
|
||||||
leadingIcon = Icons.person;
|
leadingIcon = Icons.person;
|
||||||
}
|
}
|
||||||
@@ -4479,7 +4529,14 @@ class _AddChatsToFolderDialogState extends State<_AddChatsToFolderDialog> {
|
|||||||
orElse: () => myId,
|
orElse: () => myId,
|
||||||
);
|
);
|
||||||
contact = widget.contacts[otherParticipantId];
|
contact = widget.contacts[otherParticipantId];
|
||||||
title = contact?.name ?? "Неизвестный";
|
|
||||||
|
if (contact != null) {
|
||||||
|
title = contact.name;
|
||||||
|
} else if (chat.title?.isNotEmpty == true) {
|
||||||
|
title = chat.title!;
|
||||||
|
} else {
|
||||||
|
title = "ID $otherParticipantId";
|
||||||
|
}
|
||||||
avatarUrl = contact?.photoBaseUrl;
|
avatarUrl = contact?.photoBaseUrl;
|
||||||
leadingIcon = Icons.person;
|
leadingIcon = Icons.person;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ class Channel {
|
|||||||
factory Channel.fromJson(Map<String, dynamic> json) {
|
factory Channel.fromJson(Map<String, dynamic> json) {
|
||||||
final names = json['names'] as List<dynamic>?;
|
final names = json['names'] as List<dynamic>?;
|
||||||
final nameData = names?.isNotEmpty == true ? names![0] : null;
|
final nameData = names?.isNotEmpty == true ? names![0] : null;
|
||||||
|
final channelId = json['id'] as int;
|
||||||
|
|
||||||
return Channel(
|
return Channel(
|
||||||
id: json['id'] as int,
|
id: channelId,
|
||||||
name: nameData?['name'] as String? ?? 'Неизвестный канал',
|
name: nameData?['name'] as String? ?? 'ID $channelId',
|
||||||
description: nameData?['description'] as String?,
|
description: nameData?['description'] as String?,
|
||||||
photoBaseUrl: json['baseUrl'] as String?,
|
photoBaseUrl: json['baseUrl'] as String?,
|
||||||
link: json['link'] as String?,
|
link: json['link'] as String?,
|
||||||
|
|||||||
@@ -31,10 +31,11 @@ class Contact {
|
|||||||
|
|
||||||
factory Contact.fromJson(Map<String, dynamic> json) {
|
factory Contact.fromJson(Map<String, dynamic> json) {
|
||||||
final nameData = json['names']?[0];
|
final nameData = json['names']?[0];
|
||||||
|
final userId = json['id'] as int;
|
||||||
|
|
||||||
String finalFirstName = '';
|
String finalFirstName = '';
|
||||||
String finalLastName = '';
|
String finalLastName = '';
|
||||||
String finalName = 'Unknown';
|
String finalName = 'ID $userId';
|
||||||
|
|
||||||
if (nameData != null) {
|
if (nameData != null) {
|
||||||
finalFirstName = nameData['firstName'] ?? '';
|
finalFirstName = nameData['firstName'] ?? '';
|
||||||
@@ -42,10 +43,9 @@ class Contact {
|
|||||||
final fullName = '$finalFirstName $finalLastName'.trim();
|
final fullName = '$finalFirstName $finalLastName'.trim();
|
||||||
finalName = fullName.isNotEmpty
|
finalName = fullName.isNotEmpty
|
||||||
? fullName
|
? fullName
|
||||||
: (nameData['name'] ?? 'Unknown');
|
: (nameData['name'] ?? 'ID $userId');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final status = json['status'];
|
final status = json['status'];
|
||||||
final isBlocked = status == 'BLOCKED';
|
final isBlocked = status == 'BLOCKED';
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_currentContact = widget.initialContact;
|
_currentContact = widget.initialContact;
|
||||||
|
|
||||||
|
|
||||||
_contactSubscription = ApiService.instance.contactUpdates.listen((contact) {
|
_contactSubscription = ApiService.instance.contactUpdates.listen((contact) {
|
||||||
if (contact.id == _currentContact.id && mounted) {
|
if (contact.id == _currentContact.id && mounted) {
|
||||||
ApiService.instance.updateCachedContact(contact);
|
ApiService.instance.updateCachedContact(contact);
|
||||||
@@ -50,17 +49,14 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_membersSubscription = ApiService.instance.messages.listen((message) {
|
_membersSubscription = ApiService.instance.messages.listen((message) {
|
||||||
if (message['type'] == 'group_members' && mounted) {
|
if (message['type'] == 'group_members' && mounted) {
|
||||||
_handleGroupMembersResponse(message['payload']);
|
_handleGroupMembersResponse(message['payload']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
_loadMembersFromCache();
|
_loadMembersFromCache();
|
||||||
|
|
||||||
|
|
||||||
if (_loadedMembers.length < 50) {
|
if (_loadedMembers.length < 50) {
|
||||||
_loadedMembers.clear();
|
_loadedMembers.clear();
|
||||||
_loadedMemberIds.clear();
|
_loadedMemberIds.clear();
|
||||||
@@ -69,7 +65,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
ApiService.instance.getGroupMembers(widget.chatId, marker: 0, count: 50);
|
ApiService.instance.getGroupMembers(widget.chatId, marker: 0, count: 50);
|
||||||
_isLoadingMembers = true;
|
_isLoadingMembers = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
_lastMarker = _loadedMembers.isNotEmpty
|
_lastMarker = _loadedMembers.isNotEmpty
|
||||||
? _loadedMembers.last['id'] as int?
|
? _loadedMembers.last['id'] as int?
|
||||||
: null;
|
: null;
|
||||||
@@ -81,7 +76,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_scrollController.addListener(_onScroll);
|
_scrollController.addListener(_onScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +103,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<dynamic> membersRaw = [];
|
List<dynamic> membersRaw = [];
|
||||||
if (currentChat['members'] is List) {
|
if (currentChat['members'] is List) {
|
||||||
membersRaw = currentChat['members'] as List<dynamic>;
|
membersRaw = currentChat['members'] as List<dynamic>;
|
||||||
@@ -163,31 +156,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _handleGroupMembersResponse(Map<String, dynamic> payload) {
|
void _handleGroupMembersResponse(Map<String, dynamic> payload) {
|
||||||
print(
|
print(
|
||||||
'DEBUG: _handleGroupMembersResponse вызван с payload: ${payload.keys}',
|
'DEBUG: _handleGroupMembersResponse вызван с payload: ${payload.keys}',
|
||||||
@@ -383,7 +351,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -426,7 +393,7 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
removableMembers.add({
|
removableMembers.add({
|
||||||
'id': id,
|
'id': id,
|
||||||
'name': contact['names']?[0]?['name'] ?? 'Неизвестный',
|
'name': contact['names']?[0]?['name'] ?? 'ID $id',
|
||||||
'contact': contact,
|
'contact': contact,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -456,7 +423,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
content: Text('Удалено ${selectedMembers.length} участников'),
|
content: Text('Удалено ${selectedMembers.length} участников'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -499,7 +465,7 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
if (contact != null) {
|
if (contact != null) {
|
||||||
promotableMembers.add({
|
promotableMembers.add({
|
||||||
'id': id,
|
'id': id,
|
||||||
'name': contact['names']?[0]?['name'] ?? 'Неизвестный',
|
'name': contact['names']?[0]?['name'] ?? 'ID $id',
|
||||||
'contact': contact,
|
'contact': contact,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -553,7 +519,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
ApiService.instance.leaveGroup(widget.chatId);
|
ApiService.instance.leaveGroup(widget.chatId);
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
..pop()
|
..pop()
|
||||||
..pop();
|
..pop();
|
||||||
@@ -708,7 +673,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
Widget _buildGroupManagementButtons() {
|
Widget _buildGroupManagementButtons() {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
|
||||||
bool amIAdmin = false;
|
bool amIAdmin = false;
|
||||||
final currentChat = _getCurrentGroupChat();
|
final currentChat = _getCurrentGroupChat();
|
||||||
if (currentChat != null) {
|
if (currentChat != null) {
|
||||||
@@ -777,7 +741,6 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: FilledButton.icon(
|
child: FilledButton.icon(
|
||||||
@@ -837,12 +800,12 @@ class _GroupSettingsScreenState extends State<GroupSettingsScreen> {
|
|||||||
final fullName = '$firstName $lastName'.trim();
|
final fullName = '$firstName $lastName'.trim();
|
||||||
name = fullName.isNotEmpty
|
name = fullName.isNotEmpty
|
||||||
? fullName
|
? fullName
|
||||||
: (nameData['name'] as String? ?? 'Неизвестный');
|
: (nameData['name'] as String? ?? 'ID $id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name == null || name.isEmpty) {
|
if (name == null || name.isEmpty) {
|
||||||
name = 'Неизвестный';
|
name = 'ID $id';
|
||||||
}
|
}
|
||||||
avatarUrl =
|
avatarUrl =
|
||||||
contact?['baseUrl'] as String? ?? contact?['baseRawUrl'] as String?;
|
contact?['baseUrl'] as String? ?? contact?['baseRawUrl'] as String?;
|
||||||
@@ -1050,7 +1013,8 @@ class _AddMemberDialogState extends State<_AddMemberDialog> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final contact = widget.contacts[index];
|
final contact = widget.contacts[index];
|
||||||
final contactId = contact['id'] as int;
|
final contactId = contact['id'] as int;
|
||||||
final contactName = contact['names']?[0]?['name'] ?? 'Неизвестный';
|
final contactName =
|
||||||
|
contact['names']?[0]?['name'] ?? 'ID $contactId';
|
||||||
final isSelected = _selectedContacts.contains(contactId);
|
final isSelected = _selectedContacts.contains(contactId);
|
||||||
|
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'package:gwid/api/api_service.dart';
|
||||||
|
|
||||||
class StorageScreen extends StatefulWidget {
|
class StorageScreen extends StatefulWidget {
|
||||||
final bool isModal;
|
final bool isModal;
|
||||||
@@ -114,12 +115,39 @@ class _StorageScreenState extends State<StorageScreen>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _clearCache() async {
|
Future<void> _clearCache() async {
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Очистить кэш'),
|
||||||
|
content: const Text(
|
||||||
|
'Это действие очистит весь кэш приложения, включая кэш сообщений, медиафайлов и аватаров. '
|
||||||
|
'Это действие нельзя отменить.',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text('Отмена'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
style: TextButton.styleFrom(foregroundColor: Colors.orange),
|
||||||
|
child: const Text('Очистить'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed != true) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ApiService.instance.clearAllCaches();
|
||||||
|
|
||||||
final cacheDir = await getTemporaryDirectory();
|
final cacheDir = await getTemporaryDirectory();
|
||||||
if (await cacheDir.exists()) {
|
if (await cacheDir.exists()) {
|
||||||
await cacheDir.delete(recursive: true);
|
await cacheDir.delete(recursive: true);
|
||||||
await cacheDir.create();
|
await cacheDir.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _loadStorageInfo();
|
await _loadStorageInfo();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class ChatCacheService {
|
|||||||
static const String _chatMessagesKey = 'cached_chat_messages';
|
static const String _chatMessagesKey = 'cached_chat_messages';
|
||||||
|
|
||||||
static const Duration _chatsTTL = Duration(hours: 1);
|
static const Duration _chatsTTL = Duration(hours: 1);
|
||||||
static const Duration _contactsTTL = Duration(hours: 6);
|
static const Duration _contactsTTL = Duration(hours: 24);
|
||||||
static const Duration _messagesTTL = Duration(hours: 2);
|
static const Duration _messagesTTL = Duration(hours: 2);
|
||||||
|
|
||||||
Future<void> cacheChats(List<Map<String, dynamic>> chats) async {
|
Future<void> cacheChats(List<Map<String, dynamic>> chats) async {
|
||||||
@@ -54,22 +54,32 @@ class ChatCacheService {
|
|||||||
.map(
|
.map(
|
||||||
(contact) => {
|
(contact) => {
|
||||||
'id': contact.id,
|
'id': contact.id,
|
||||||
'name': contact.name,
|
'names': [
|
||||||
'firstName': contact.firstName,
|
{
|
||||||
'lastName': contact.lastName,
|
'name': contact.name,
|
||||||
|
'firstName': contact.firstName,
|
||||||
|
'lastName': contact.lastName,
|
||||||
|
'type': 'ONEME',
|
||||||
|
},
|
||||||
|
],
|
||||||
'photoBaseUrl': contact.photoBaseUrl,
|
'photoBaseUrl': contact.photoBaseUrl,
|
||||||
|
'baseUrl': contact.photoBaseUrl,
|
||||||
'isBlocked': contact.isBlocked,
|
'isBlocked': contact.isBlocked,
|
||||||
'isBlockedByMe': contact.isBlockedByMe,
|
'isBlockedByMe': contact.isBlockedByMe,
|
||||||
'accountStatus': contact.accountStatus,
|
'accountStatus': contact.accountStatus,
|
||||||
'status': contact.status,
|
'status': contact.status,
|
||||||
|
'options': contact.options,
|
||||||
|
'description': contact.description,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
await _cacheService.set(_contactsKey, contactsData, ttl: _contactsTTL);
|
await _cacheService.set(_contactsKey, contactsData, ttl: _contactsTTL);
|
||||||
print('Кэшировано ${contacts.length} контактов');
|
print(
|
||||||
|
'✅ Кэшировано ${contacts.length} контактов (глобально) с описаниями',
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Ошибка кэширования контактов: $e');
|
print('❌ Ошибка кэширования контактов: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +90,65 @@ class ChatCacheService {
|
|||||||
ttl: _contactsTTL,
|
ttl: _contactsTTL,
|
||||||
);
|
);
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
return cached.map((data) => Contact.fromJson(data)).toList();
|
final contacts = cached.map((data) => Contact.fromJson(data)).toList();
|
||||||
|
print('✅ Загружено ${contacts.length} контактов из глобального кэша');
|
||||||
|
return contacts;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Ошибка получения кэшированных контактов: $e');
|
print('❌ Ошибка получения кэшированных контактов: $e');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кэширование контактов для конкретного чата
|
||||||
|
Future<void> cacheChatContacts(int chatId, List<Contact> contacts) async {
|
||||||
|
try {
|
||||||
|
final key = 'chat_contacts_$chatId';
|
||||||
|
final contactsData = contacts
|
||||||
|
.map(
|
||||||
|
(contact) => {
|
||||||
|
'id': contact.id,
|
||||||
|
'names': [
|
||||||
|
{
|
||||||
|
'name': contact.name,
|
||||||
|
'firstName': contact.firstName,
|
||||||
|
'lastName': contact.lastName,
|
||||||
|
'type': 'ONEME',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'photoBaseUrl': contact.photoBaseUrl,
|
||||||
|
'baseUrl': contact.photoBaseUrl,
|
||||||
|
'isBlocked': contact.isBlocked,
|
||||||
|
'isBlockedByMe': contact.isBlockedByMe,
|
||||||
|
'accountStatus': contact.accountStatus,
|
||||||
|
'status': contact.status,
|
||||||
|
'options': contact.options,
|
||||||
|
'description': contact.description,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
await _cacheService.set(key, contactsData, ttl: _contactsTTL);
|
||||||
|
print('✅ Кэшировано ${contacts.length} контактов для чата $chatId');
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Ошибка кэширования контактов для чата $chatId: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Contact>?> getCachedChatContacts(int chatId) async {
|
||||||
|
try {
|
||||||
|
final key = 'chat_contacts_$chatId';
|
||||||
|
final cached = await _cacheService.get<List<dynamic>>(
|
||||||
|
key,
|
||||||
|
ttl: _contactsTTL,
|
||||||
|
);
|
||||||
|
if (cached != null) {
|
||||||
|
final contacts = cached.map((data) => Contact.fromJson(data)).toList();
|
||||||
|
print('✅ Загружено ${contacts.length} контактов из кэша чата $chatId');
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Ошибка получения кэшированных контактов для чата $chatId: $e');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -248,13 +313,14 @@ class ChatCacheService {
|
|||||||
'$_chatMessagesKey$chatId',
|
'$_chatMessagesKey$chatId',
|
||||||
'chat_info_$chatId',
|
'chat_info_$chatId',
|
||||||
'last_message_$chatId',
|
'last_message_$chatId',
|
||||||
|
'chat_contacts_$chatId',
|
||||||
];
|
];
|
||||||
|
|
||||||
for (final key in keys) {
|
for (final key in keys) {
|
||||||
await _cacheService.remove(key);
|
await _cacheService.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
print('Кэш для чата $chatId очищен');
|
print('Кэш для чата $chatId очищен (включая контакты)');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Ошибка очистки кэша для чата $chatId: $e');
|
print('Ошибка очистки кэша для чата $chatId: $e');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,10 +283,12 @@ class ChatMessageBubble extends StatelessWidget {
|
|||||||
if (originalSenderId != null && cache != null) {
|
if (originalSenderId != null && cache != null) {
|
||||||
final originalSenderContact = cache[originalSenderId];
|
final originalSenderContact = cache[originalSenderId];
|
||||||
forwardedSenderName =
|
forwardedSenderName =
|
||||||
originalSenderContact?.name ?? 'Участник $originalSenderId';
|
originalSenderContact?.name ?? 'ID $originalSenderId';
|
||||||
forwardedSenderAvatarUrl ??= originalSenderContact?.photoBaseUrl;
|
forwardedSenderAvatarUrl ??= originalSenderContact?.photoBaseUrl;
|
||||||
|
} else if (originalSenderId != null) {
|
||||||
|
forwardedSenderName = 'ID $originalSenderId';
|
||||||
} else {
|
} else {
|
||||||
forwardedSenderName = 'Неизвестный';
|
forwardedSenderName = 'Пользователь';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -613,8 +615,8 @@ class ChatMessageBubble extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
replySenderId != null
|
replySenderId != null
|
||||||
? (contactDetailsCache?[replySenderId]?.name ??
|
? (contactDetailsCache?[replySenderId]?.name ??
|
||||||
'Участник $replySenderId')
|
'ID $replySenderId')
|
||||||
: 'Неизвестный',
|
: 'Пользователь',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -1294,7 +1296,7 @@ class ChatMessageBubble extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 2.0, bottom: 2.0),
|
padding: const EdgeInsets.only(left: 2.0, bottom: 2.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
senderName ?? 'Неизвестный',
|
senderName!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: _getUserColor(
|
color: _getUserColor(
|
||||||
@@ -3623,7 +3625,7 @@ class ChatMessageBubble extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 2.0, bottom: 2.0),
|
padding: const EdgeInsets.only(left: 2.0, bottom: 2.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
senderName ?? 'Неизвестный',
|
senderName!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: _getUserColor(
|
color: _getUserColor(
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class ControlMessageChip extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final eventType = controlAttach['event'];
|
final eventType = controlAttach['event'];
|
||||||
final senderName = contacts[message.senderId]?.name ?? 'Неизвестный';
|
final senderName =
|
||||||
|
contacts[message.senderId]?.name ?? 'ID ${message.senderId}';
|
||||||
final isMe = message.senderId == myId;
|
final isMe = message.senderId == myId;
|
||||||
final senderDisplayName = isMe ? 'Вы' : senderName;
|
final senderDisplayName = isMe ? 'Вы' : senderName;
|
||||||
|
|
||||||
@@ -297,7 +298,14 @@ class MessagePreviewDialog {
|
|||||||
orElse: () => myId,
|
orElse: () => myId,
|
||||||
);
|
);
|
||||||
final contact = contacts[otherParticipantId];
|
final contact = contacts[otherParticipantId];
|
||||||
return contact?.name ?? "Неизвестный чат";
|
|
||||||
|
if (contact != null) {
|
||||||
|
return contact.name;
|
||||||
|
} else if (chat.title?.isNotEmpty == true) {
|
||||||
|
return chat.title!;
|
||||||
|
} else {
|
||||||
|
return "ID $otherParticipantId";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,7 +552,8 @@ class MessagePreviewDialog {
|
|||||||
contacts[message.senderId];
|
contacts[message.senderId];
|
||||||
final senderName = isMe
|
final senderName = isMe
|
||||||
? 'Вы'
|
? 'Вы'
|
||||||
: (senderContact?.name ?? 'Неизвестный');
|
: (senderContact?.name ??
|
||||||
|
'ID ${message.senderId}');
|
||||||
|
|
||||||
String? forwardedFrom;
|
String? forwardedFrom;
|
||||||
String? forwardedFromAvatarUrl;
|
String? forwardedFromAvatarUrl;
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ class PinnedMessageWidget extends StatelessWidget {
|
|||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final senderName =
|
final senderName =
|
||||||
contacts[pinnedMessage.senderId]?.name ??
|
contacts[pinnedMessage.senderId]?.name ??
|
||||||
(pinnedMessage.senderId == myId ? 'Вы' : 'Неизвестный');
|
(pinnedMessage.senderId == myId
|
||||||
|
? 'Вы'
|
||||||
|
: 'ID ${pinnedMessage.senderId}');
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 0),
|
margin: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 0),
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gwid/services/avatar_cache_service.dart';
|
import 'package:gwid/services/avatar_cache_service.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfilePanel extends StatefulWidget {
|
class UserProfilePanel extends StatefulWidget {
|
||||||
final int userId;
|
final int userId;
|
||||||
final String? name;
|
final String? name;
|
||||||
@@ -37,16 +34,16 @@ class UserProfilePanel extends StatefulWidget {
|
|||||||
class _UserProfilePanelState extends State<UserProfilePanel> {
|
class _UserProfilePanelState extends State<UserProfilePanel> {
|
||||||
final ScrollController _nameScrollController = ScrollController();
|
final ScrollController _nameScrollController = ScrollController();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
String get _displayName {
|
String get _displayName {
|
||||||
if (widget.firstName != null || widget.lastName != null) {
|
if (widget.firstName != null || widget.lastName != null) {
|
||||||
final firstName = widget.firstName ?? '';
|
final firstName = widget.firstName ?? '';
|
||||||
final lastName = widget.lastName ?? '';
|
final lastName = widget.lastName ?? '';
|
||||||
final fullName = '$firstName $lastName'.trim();
|
final fullName = '$firstName $lastName'.trim();
|
||||||
return fullName.isNotEmpty ? fullName : (widget.name ?? 'Неизвестный');
|
return fullName.isNotEmpty
|
||||||
|
? fullName
|
||||||
|
: (widget.name ?? 'ID ${widget.userId}');
|
||||||
}
|
}
|
||||||
return widget.name ?? 'Неизвестный';
|
return widget.name ?? 'ID ${widget.userId}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -108,122 +105,6 @@ class _UserProfilePanelState extends State<UserProfilePanel> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|||||||
Reference in New Issue
Block a user