You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
544 lines
16 KiB
544 lines
16 KiB
import 'package:get/get.dart';
|
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
|
|
import '../../im/im_manager.dart';
|
|
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
|
|
import 'conversation_controller.dart';
|
|
|
|
class ChatController extends GetxController {
|
|
final String userId;
|
|
|
|
// 用户信息
|
|
final Rx<EMUserInfo?> userInfo = Rx<EMUserInfo?>(null);
|
|
|
|
// 消息列表
|
|
final RxList<EMMessage> messages = RxList<EMMessage>([]);
|
|
|
|
// 加载更多的游标
|
|
String? _cursor;
|
|
|
|
// 视频发送状态
|
|
final RxBool isSendingVideo = RxBool(false);
|
|
final RxString sendingStatus = RxString('');
|
|
|
|
ChatController({required this.userId});
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
// 注册到 IMManager,以便接收消息时能通知到此 Controller
|
|
IMManager.instance.registerChatController(this);
|
|
// 初始化时获取用户信息和消息列表
|
|
fetchUserInfo();
|
|
fetchMessages();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 注销 ChatController
|
|
IMManager.instance.unregisterChatController(userId);
|
|
super.onClose();
|
|
}
|
|
|
|
/// 获取用户信息
|
|
Future<void> fetchUserInfo() async {
|
|
try {
|
|
final EMUserInfo? info = await IMManager.instance.getUserInfo(userId);
|
|
if (info != null) {
|
|
userInfo.value = info;
|
|
update(); // 通知 GetBuilder 更新 UI
|
|
if (Get.isLogEnable) {
|
|
Get.log('获取用户信息成功: ${info.nickName}');
|
|
}
|
|
} else {
|
|
if (Get.isLogEnable) {
|
|
Get.log('未找到用户信息: $userId');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('获取用户信息失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 发送消息
|
|
Future<bool> sendMessage(String content) async {
|
|
try {
|
|
// 先创建消息对象(即使发送失败也要显示在列表中)
|
|
final tempMessage = EMMessage.createTxtSendMessage(
|
|
targetId: userId,
|
|
content: content,
|
|
);
|
|
|
|
// 将消息添加到列表末尾(显示发送中状态)
|
|
messages.add(tempMessage);
|
|
update();
|
|
|
|
// 尝试发送消息
|
|
final message = await IMManager.instance.sendTextMessage(content, userId);
|
|
if (message != null) {
|
|
// 发送成功,替换临时消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId);
|
|
if (index != -1) {
|
|
messages[index] = message;
|
|
}
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
} else {
|
|
// 发送失败,消息状态会自动变为FAIL
|
|
update();
|
|
SmartDialog.showToast('消息发送失败,请点击重发');
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('发送消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('消息发送失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 发送图片消息
|
|
Future<bool> sendImageMessage(String imagePath) async {
|
|
try {
|
|
// 先创建消息对象(即使发送失败也要显示在列表中)
|
|
final tempMessage = EMMessage.createImageSendMessage(
|
|
targetId: userId,
|
|
filePath: imagePath,
|
|
sendOriginalImage: false,
|
|
);
|
|
|
|
// 将消息添加到列表末尾(显示发送中状态)
|
|
messages.add(tempMessage);
|
|
update();
|
|
|
|
// 尝试发送消息
|
|
final message = await IMManager.instance.sendImageMessage(
|
|
imagePath,
|
|
userId,
|
|
);
|
|
if (message != null) {
|
|
// 发送成功,替换临时消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId);
|
|
if (index != -1) {
|
|
messages[index] = message;
|
|
}
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
} else {
|
|
// 发送失败,消息状态会自动变为FAIL
|
|
update();
|
|
SmartDialog.showToast('图片发送失败,请点击重发');
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('发送图片消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('图片发送失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 发送语音消息
|
|
Future<bool> sendVoiceMessage(String filePath, int seconds) async {
|
|
try {
|
|
// 先创建消息对象(即使发送失败也要显示在列表中)
|
|
final tempMessage = EMMessage.createVoiceSendMessage(
|
|
targetId: userId,
|
|
filePath: filePath,
|
|
duration: seconds,
|
|
);
|
|
|
|
// 将消息添加到列表末尾(显示发送中状态)
|
|
messages.add(tempMessage);
|
|
update();
|
|
|
|
// 尝试发送消息
|
|
final message = await IMManager.instance.sendVoiceMessage(
|
|
filePath,
|
|
userId,
|
|
seconds,
|
|
);
|
|
if (message != null) {
|
|
// 发送成功,替换临时消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId);
|
|
if (index != -1) {
|
|
messages[index] = message;
|
|
}
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
} else {
|
|
// 发送失败,消息状态会自动变为FAIL
|
|
update();
|
|
SmartDialog.showToast('语音发送失败,请点击重发');
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('发送语音消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('语音发送失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 发送视频消息
|
|
Future<bool> sendVideoMessage(String filePath, int duration) async {
|
|
try {
|
|
print('🎬 [ChatController] 准备发送视频消息');
|
|
print('视频路径: $filePath');
|
|
print('视频时长: $duration 秒');
|
|
|
|
// 先创建消息对象(即使发送失败也要显示在列表中)
|
|
final tempMessage = EMMessage.createVideoSendMessage(
|
|
targetId: userId,
|
|
filePath: filePath,
|
|
duration: duration,
|
|
);
|
|
|
|
// 将消息添加到列表末尾(显示发送中状态)
|
|
messages.add(tempMessage);
|
|
update();
|
|
|
|
// 尝试发送消息(后台上传,消息状态会自动更新)
|
|
final message = await IMManager.instance.sendVideoMessage(
|
|
filePath,
|
|
userId,
|
|
duration,
|
|
);
|
|
|
|
if (message != null) {
|
|
print('✅ [ChatController] 视频消息发送成功');
|
|
// 发送成功,替换临时消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == tempMessage.msgId);
|
|
if (index != -1) {
|
|
messages[index] = message;
|
|
}
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
} else {
|
|
// 发送失败,消息状态会自动变为FAIL
|
|
update();
|
|
SmartDialog.showToast('视频发送失败,请点击重发');
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('发送视频消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('视频发送失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 获取消息列表
|
|
Future<void> fetchMessages({bool loadMore = false}) async {
|
|
try {
|
|
// 如果加载更多但没有游标,说明已经加载完所有消息
|
|
if (loadMore && _cursor == null) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('没有更多消息了');
|
|
}
|
|
return;
|
|
}
|
|
|
|
final List<EMMessage?> fetchedMessages = await IMManager.instance
|
|
.getMessages(
|
|
userId,
|
|
pageSize: 20,
|
|
startMsgId: loadMore ? _cursor : null,
|
|
);
|
|
|
|
// 过滤掉null消息
|
|
final List<EMMessage> validMessages = fetchedMessages
|
|
.whereType<EMMessage>()
|
|
.toList();
|
|
|
|
if (loadMore) {
|
|
// 加载更多时,需要去重并添加到列表开头(更旧的消息)
|
|
final existingMsgIds = messages.map((msg) => msg.msgId).toSet();
|
|
final newMessages = validMessages
|
|
.where((msg) => !existingMsgIds.contains(msg.msgId))
|
|
.toList();
|
|
|
|
if (newMessages.isNotEmpty) {
|
|
messages.insertAll(0, newMessages);
|
|
// 更新游标为最旧的消息ID(列表开头)
|
|
_cursor = newMessages.first.msgId;
|
|
if (Get.isLogEnable) {
|
|
Get.log('加载更多消息成功,新增: ${newMessages.length} 条,总数量: ${messages.length}');
|
|
}
|
|
} else {
|
|
// 没有新消息,说明已经加载完所有消息
|
|
_cursor = null;
|
|
if (Get.isLogEnable) {
|
|
Get.log('没有更多消息了');
|
|
}
|
|
}
|
|
} else {
|
|
// 刷新时替换整个列表
|
|
messages.assignAll(validMessages);
|
|
// 更新游标为最旧的消息ID(列表开头)
|
|
if (validMessages.isNotEmpty) {
|
|
_cursor = validMessages.first.msgId;
|
|
} else {
|
|
_cursor = null;
|
|
}
|
|
if (Get.isLogEnable) {
|
|
Get.log('刷新消息成功,数量: ${validMessages.length}');
|
|
}
|
|
}
|
|
|
|
// 通知UI更新
|
|
update();
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('获取消息失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 加载更多消息
|
|
Future<void> loadMoreMessages() async {
|
|
if (_cursor != null) {
|
|
await fetchMessages(loadMore: true);
|
|
}
|
|
}
|
|
|
|
/// 检查是否还有更多消息可以加载
|
|
bool hasMoreMessages() {
|
|
return _cursor != null;
|
|
}
|
|
|
|
/// 添加接收到的消息
|
|
void addReceivedMessage(EMMessage message) {
|
|
// 检查消息是否已存在(避免重复添加)
|
|
if (!messages.any((msg) => msg.msgId == message.msgId)) {
|
|
// 将新消息添加到列表末尾
|
|
messages.add(message);
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
|
|
if (Get.isLogEnable) {
|
|
Get.log('收到新消息并添加到列表: ${message.msgId}');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 撤回消息
|
|
Future<bool> recallMessage(EMMessage message) async {
|
|
try {
|
|
final success = await IMManager.instance.recallMessage(message);
|
|
if (success) {
|
|
// 从消息列表中移除或更新消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == message.msgId);
|
|
if (index != -1) {
|
|
// 可以保留消息但标记为已撤回,或者直接移除
|
|
// 这里选择移除消息
|
|
messages.removeAt(index);
|
|
update();
|
|
}
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
SmartDialog.showToast('消息已撤回');
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('撤回消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('撤回消息失败');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 删除消息
|
|
Future<bool> deleteMessage(EMMessage message, {bool deleteRemote = false}) async {
|
|
try {
|
|
final conversationId = message.conversationId ?? userId;
|
|
final messageId = message.msgId;
|
|
if (messageId.isEmpty) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('消息ID为空,无法删除');
|
|
}
|
|
return false;
|
|
}
|
|
final success = await IMManager.instance.deleteMessage(conversationId, messageId, deleteRemote: deleteRemote);
|
|
if (success) {
|
|
// 从消息列表中移除
|
|
final index = messages.indexWhere((msg) => msg.msgId == message.msgId);
|
|
if (index != -1) {
|
|
messages.removeAt(index);
|
|
update();
|
|
}
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
SmartDialog.showToast('消息已删除');
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('删除消息失败: $e');
|
|
}
|
|
SmartDialog.showToast('删除消息失败');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 标记消息为已读
|
|
Future<bool> markMessageAsRead(String messageId) async {
|
|
try {
|
|
final success = await IMManager.instance.markMessageAsRead(userId, messageId);
|
|
if (success) {
|
|
// 更新消息的已读状态
|
|
final index = messages.indexWhere((msg) => msg.msgId == messageId);
|
|
if (index != -1) {
|
|
update();
|
|
}
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('标记消息为已读失败: $e');
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 标记会话所有消息为已读
|
|
Future<bool> markAllMessagesAsRead() async {
|
|
try {
|
|
final success = await IMManager.instance.markAllMessagesAsRead(userId);
|
|
if (success) {
|
|
// 更新所有消息的已读状态
|
|
update();
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
return true;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('标记所有消息为已读失败: $e');
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 处理消息撤回通知
|
|
void handleMessageRecalled(EMMessage message) {
|
|
try {
|
|
// 从消息列表中移除或更新消息
|
|
final index = messages.indexWhere((msg) => msg.msgId == message.msgId);
|
|
if (index != -1) {
|
|
messages.removeAt(index);
|
|
update();
|
|
}
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
if (Get.isLogEnable) {
|
|
Get.log('处理消息撤回: ${message.msgId}');
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('处理消息撤回失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 更新消息状态
|
|
void updateMessageStatus(EMMessage message, MessageStatus status) {
|
|
try {
|
|
final index = messages.indexWhere((msg) => msg.msgId == message.msgId);
|
|
if (index != -1) {
|
|
// 更新消息状态
|
|
update();
|
|
if (Get.isLogEnable) {
|
|
Get.log('更新消息状态: ${message.msgId}, status=$status');
|
|
}
|
|
}
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('更新消息状态失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 检查消息是否发送失败
|
|
bool isMessageFailed(EMMessage message) {
|
|
return message.status == MessageStatus.FAIL;
|
|
}
|
|
|
|
/// 重发消息
|
|
Future<bool> resendMessage(EMMessage failedMessage) async {
|
|
try {
|
|
if (Get.isLogEnable) {
|
|
Get.log('🔄 [ChatController] 开始重发消息: ${failedMessage.msgId}');
|
|
}
|
|
|
|
// 调用IMManager的重发方法
|
|
final newMessage = await IMManager.instance.resendMessage(failedMessage);
|
|
if (newMessage != null) {
|
|
// 找到原消息并替换
|
|
final index = messages.indexWhere((msg) => msg.msgId == failedMessage.msgId);
|
|
if (index != -1) {
|
|
messages[index] = newMessage;
|
|
update();
|
|
if (Get.isLogEnable) {
|
|
Get.log('✅ [ChatController] 消息重发成功');
|
|
}
|
|
// 更新会话列表
|
|
_refreshConversationList();
|
|
SmartDialog.showToast('消息已重发');
|
|
return true;
|
|
}
|
|
} else {
|
|
if (Get.isLogEnable) {
|
|
Get.log('❌ [ChatController] 消息重发失败');
|
|
}
|
|
SmartDialog.showToast('消息重发失败,请重试');
|
|
return false;
|
|
}
|
|
return false;
|
|
} catch (e) {
|
|
if (Get.isLogEnable) {
|
|
Get.log('❌ [ChatController] 重发消息异常: $e');
|
|
}
|
|
SmartDialog.showToast('重发消息失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 刷新会话列表
|
|
void _refreshConversationList() {
|
|
try {
|
|
// 尝试获取 ConversationController 并刷新会话列表
|
|
if (Get.isRegistered<ConversationController>()) {
|
|
final conversationController = Get.find<ConversationController>();
|
|
conversationController.refreshConversations();
|
|
}
|
|
} catch (e) {
|
|
// ConversationController 可能未注册,忽略错误
|
|
if (Get.isLogEnable) {
|
|
Get.log('刷新会话列表失败: $e');
|
|
}
|
|
}
|
|
}
|
|
}
|