бля а какой запрос на поиск по айди

This commit is contained in:
Jganenokk
2026-04-11 00:00:37 +07:00
parent 94dcd670a3
commit d0e8d64967
5 changed files with 288 additions and 24 deletions
+75 -13
View File
@@ -1,10 +1,20 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import '../api.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/storage/app_database.dart';
import '../../models/attachment.dart';
class ContactCache {
static final Map<int, String> _cache = {};
static void put(int id, String name) {
_cache[id] = name;
}
static String? get(int id) => _cache[id];
}
class CachedMessage {
final String id;
final int accountId;
@@ -39,11 +49,20 @@ class CachedMessage {
List<MessageAttachment>? attachments;
if (payload != null) {
final attaches = payload['attaches'] as List?;
if (attaches != null) {
attachments = attaches
.map((a) => MessageAttachment.fromMap(a as Map<String, dynamic>))
.toList();
final linkType = payload['link']?['type'] as String?;
if (linkType == 'FORWARD') {
attachments = [ForwardedMessageAttachment.fromMap(payload)];
} else {
final attaches = payload['attaches'] as List?;
if (attaches != null) {
attachments = attaches
.map(
(a) => MessageAttachment.fromMap(
Map<String, dynamic>.from(a as Map),
),
)
.toList();
}
}
}
@@ -158,13 +177,24 @@ class MessagesModule {
final id = m['id']?.toString();
if (id == null) return null;
final attaches = m['attaches'] as List?;
final linkRaw = m['link'];
String? linkType;
if (linkRaw is Map) {
linkType = linkRaw['type'] as String?;
}
List<MessageAttachment>? attachments;
if (attaches != null) {
attachments = attaches
.whereType<Map>()
.map((a) => MessageAttachment.fromMap(a.cast<String, dynamic>()))
.toList();
if (linkType == 'FORWARD') {
final fwdMap = Map<String, dynamic>.from(m.cast());
attachments = [ForwardedMessageAttachment.fromMap(fwdMap)];
} else {
final attaches = m['attaches'] as List?;
if (attaches != null) {
attachments = attaches
.whereType<Map>()
.map((a) => MessageAttachment.fromMap(Map<String, dynamic>.from(a)))
.toList();
}
}
return CachedMessage(
@@ -175,7 +205,7 @@ class MessagesModule {
text: m['text'] as String?,
time: (m['time'] as int?) ?? 0,
status: m['status'] as String?,
payload: m.cast<String, dynamic>(),
payload: Map<String, dynamic>.from(m.cast()),
attachments: attachments,
);
}
@@ -313,4 +343,36 @@ class MessagesModule {
return null;
}
}
Future<String?> searchContactById(int contactId) async {
final cached = ContactCache.get(contactId);
if (cached != null) return cached;
try {
final response = await _api.sendRequest(Opcode.contactInfo, {
'id': contactId,
});
if (!response.isOk) return null;
final data = response.payload;
if (data is! Map) return null;
final names = data['names'] as List?;
if (names != null && names.isNotEmpty) {
final name = names.first;
if (name is Map) {
final firstName = name['firstName'] as String? ?? '';
final lastName = name['lastName'] as String?;
final fullName = lastName != null
? '$firstName $lastName'
: firstName;
ContactCache.put(contactId, fullName);
return fullName;
}
}
} catch (e) {
debugPrint('searchContactById error: $e');
}
return null;
}
}
@@ -5,6 +5,7 @@ import '../../../main.dart';
import '../../../backend/api.dart';
import '../../../backend/modules/messages.dart';
import '../../../core/storage/app_database.dart';
import '../../../models/attachment.dart';
import '../../widgets/message_bubble.dart';
class ChatScreen extends StatefulWidget {
@@ -81,6 +82,7 @@ class _ChatScreenState extends State<ChatScreen>
_isLoading = false;
});
}
_loadForwardedSenderNames();
} catch (e) {
debugPrint('Error fetching history: $e');
if (mounted) {
@@ -164,6 +166,62 @@ class _ChatScreenState extends State<ChatScreen>
}
}
Future<void> _loadForwardedSenderNames() async {
final forwardIds = <int>{};
for (final msg in _messages) {
if (msg.attachments != null) {
for (final a in msg.attachments!) {
if (a is ForwardedMessageAttachment) {
if (a.originalSenderName == null) {
forwardIds.add(a.originalSenderId);
}
}
}
}
}
if (forwardIds.isEmpty) return;
for (final id in forwardIds) {
final name = await messagesModule.searchContactById(id);
if (name != null && mounted) {
setState(() {
for (var i = 0; i < _messages.length; i++) {
final msg = _messages[i];
if (msg.attachments != null) {
final newAttaches = msg.attachments!.map((a) {
if (a is ForwardedMessageAttachment &&
a.originalSenderId == id &&
a.originalSenderName == null) {
return ForwardedMessageAttachment(
originalSenderId: id,
originalSenderName: name,
originalMessageId: a.originalMessageId,
originalTime: a.originalTime,
originalText: a.originalText,
originalChatId: a.originalChatId,
originalAttachments: a.originalAttachments,
);
}
return a;
}).toList();
_messages[i] = CachedMessage(
id: msg.id,
accountId: msg.accountId,
chatId: msg.chatId,
senderId: msg.senderId,
text: msg.text,
time: msg.time,
status: msg.status,
payload: msg.payload,
attachments: newAttaches,
);
}
}
});
}
}
}
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
+80 -3
View File
@@ -78,6 +78,7 @@ class MessageBubble extends StatelessWidget {
MessageType get contentType {
if (message.attachments != null && message.attachments!.isNotEmpty) {
final first = message.attachments!.first;
if (first is ForwardedMessageAttachment) return MessageType.text;
if (first is UnknownAttachment) return MessageType.text;
if (first.type == AttachmentType.audio) return MessageType.voice;
return MessageType.attachment;
@@ -317,14 +318,26 @@ class MessageBubble extends StatelessWidget {
? Colors.white
: (isDark ? cs.onSurface : const Color(0xFF1C1C1E));
final forwarded = _getForwardedAttachment();
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
child: Text(
message.text ?? '',
style: TextStyle(color: textColor, fontSize: 16, height: 1.3),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (forwarded != null) ...[
_buildForwardedHeader(context, forwarded),
const SizedBox(height: 4),
],
Text(
message.text ?? '',
style: TextStyle(color: textColor, fontSize: 16, height: 1.3),
),
],
),
),
const SizedBox(width: 8),
@@ -343,12 +356,76 @@ class MessageBubble extends StatelessWidget {
);
}
ForwardedMessageAttachment? _getForwardedAttachment() {
if (message.attachments == null || message.attachments!.isEmpty)
return null;
for (final a in message.attachments!) {
if (a is ForwardedMessageAttachment) return a;
}
return null;
}
Widget _buildForwardedHeader(
BuildContext context,
ForwardedMessageAttachment forwarded,
) {
final cs = Theme.of(context).colorScheme;
final isDark = cs.brightness == Brightness.dark;
final textColor = isMe
? Colors.white
: (isDark ? cs.onSurface : const Color(0xFF1C1C1E));
final headerColor = isMe
? Colors.white.withValues(alpha: 0.7)
: (isDark ? cs.onSurfaceVariant : const Color(0xFF8E8E93));
final senderName = forwarded.originalSenderName;
final displaySender = senderName ?? forwarded.originalSenderId.toString();
final origText = forwarded.originalText;
final hasOrigText = origText != null && origText.isNotEmpty;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Symbols.forward, size: 14, color: headerColor),
const SizedBox(width: 4),
Text(
displaySender,
style: TextStyle(
color: headerColor,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
],
),
if (hasOrigText) ...[
const SizedBox(height: 2),
Text(
origText,
style: TextStyle(color: textColor, fontSize: 14),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
],
);
}
Widget _buildAttachmentContent(BuildContext context) {
final attachments = message.attachments;
if (attachments == null || attachments.isEmpty) {
return _buildTextContent(context);
}
final first = attachments.first;
if (first is ForwardedMessageAttachment) {
return _buildTextContent(context);
}
final photos = attachments.whereType<PhotoAttachment>().toList();
if (photos.isEmpty) {
return _buildGenericAttachment(context, attachments.first);
+67
View File
@@ -433,6 +433,73 @@ class ControlAttachment extends MessageAttachment {
};
}
class ForwardedMessageAttachment extends MessageAttachment {
final int originalSenderId;
final String? originalSenderName;
final String? originalMessageId;
final int? originalTime;
final String? originalText;
final int? originalChatId;
final List<MessageAttachment>? originalAttachments;
const ForwardedMessageAttachment({
required this.originalSenderId,
this.originalSenderName,
this.originalMessageId,
this.originalTime,
this.originalText,
this.originalChatId,
this.originalAttachments,
}) : super(type: AttachmentType.photo);
factory ForwardedMessageAttachment.fromMap(Map<String, dynamic> map) {
final linkRaw = map['link'];
Map<String, dynamic>? link;
if (linkRaw is Map) {
link = Map<String, dynamic>.from(linkRaw);
}
Map<String, dynamic>? message;
if (link != null) {
final msgRaw = link['message'];
if (msgRaw is Map) {
message = Map<String, dynamic>.from(msgRaw);
}
}
List<MessageAttachment>? originalAttaches;
if (message != null) {
final attaches = message['attaches'] as List?;
if (attaches != null) {
originalAttaches = attaches
.whereType<Map>()
.map((a) => MessageAttachment.fromMap(Map<String, dynamic>.from(a)))
.toList();
}
}
return ForwardedMessageAttachment(
originalSenderId: (message?['sender'] as int?) ?? 0,
originalMessageId: message?['id']?.toString(),
originalTime: message?['time'] as int?,
originalText: message?['text'] as String?,
originalChatId: link?['chatId'] as int?,
originalAttachments: originalAttaches,
);
}
@override
Map<String, dynamic> toMap() => {
'_type': 'FORWARD',
'originalSenderId': originalSenderId,
'originalSenderName': originalSenderName,
'originalMessageId': originalMessageId,
'originalTime': originalTime,
'originalText': originalText,
'originalChatId': originalChatId,
};
}
class UnknownAttachment extends MessageAttachment {
final Map<String, dynamic> rawData;
+8 -8
View File
@@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
clock:
dependency: transitive
description:
@@ -313,18 +313,18 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.17"
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.13.0"
material_symbols_icons:
dependency: "direct main"
description:
@@ -638,10 +638,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev"
source: hosted
version: "0.7.7"
version: "0.7.10"
timezone:
dependency: "direct main"
description: