import 'package:get/get.dart'; import 'package:loopin/IM/controller/chat_controller.dart'; import 'package:loopin/IM/controller/tab_bar_controller.dart'; import 'package:loopin/IM/im_service.dart'; import 'package:loopin/models/conversation_type.dart'; import 'package:loopin/models/conversation_view_model.dart'; import 'package:loopin/models/tab_type.dart'; import 'package:loopin/pages/chat/notify_controller/notify_no_friend_controller.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart'; import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation_filter.dart'; import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart'; class GlobalBadge extends GetxController { /// 全局未读消息总数 RxInt totalUnread = 0.obs; /// 监听器对象(用于 add/remove) late final V2TimConversationListener _listener; void rest() { totalUnread.value = 0; } @override void onInit() { super.onInit(); _listener = V2TimConversationListener( onTotalUnreadMessageCountChanged: (int count) { logger.i('未读数发生变化$count'); totalUnread.value = count; Get.find().setBadge(TabType.chat, totalUnread.value); }, onNewConversation: (List conversationList) { for (var conv in conversationList) { logger.e('新创建会话:${conv.toLogString()}'); logger.i("新会话分组类型:${conv.conversationGroupList}"); handleCoverstion(conv); } }, onConversationChanged: (List conversationList) async { logger.w('会话变更:会话分组:${conversationList.first.conversationGroupList},会话内容${conversationList.first.toLogString()}'); final ctl = Get.find(); logger.w('当前会话列表内容:${ctl.chatList.length}'); final updatedIds = conversationList.map((e) => e.conversationID).toSet(); logger.w('要变更的会话id:$updatedIds'); // 收集可能存在后续分页的会话 final List willInsert = []; for (int i = 0; i < ctl.chatList.length; i++) { final chatItem = ctl.chatList[i]; logger.w('需要更新的ID:${chatItem.conversation.conversationID}'); if (updatedIds.contains(chatItem.conversation.conversationID)) { logger.w('chatList中包含:${chatItem.conversation.conversationID}'); // 从onchange中找到原始数据 final updatedConv = conversationList.firstWhere( (c) => c.conversationID == chatItem.conversation.conversationID, orElse: () => V2TimConversation(conversationID: ''), ); // 同步更新 ctl.getNoFriendData(csion: chatItem.conversation); if (updatedConv.conversationID != '' && (updatedConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false)) { // 单独处理陌生人会话 final unread = await ImService.instance.getUnreadMessageCountByFilter( filter: V2TimConversationFilter( conversationGroup: ConversationType.noFriend.name, hasUnreadCount: true, ), ); chatItem.conversation.lastMessage = updatedConv.lastMessage; chatItem.conversation.unreadCount = unread.data; // 获取陌生人未读总数 update(); } else if (updatedConv.conversationID != '') { // 其他类型统一更新处理 logger.w('非陌生人消息的会话,正常更新'); chatItem.conversation = updatedConv; update(); } } else { logger.e('本地会话列表中不包含的会话:$updatedIds'); // 情况1:nofriend分组会话,情况2:会话分页未拉取到或者分组变更 // 检测这条会话数据远端是否存在 for (var cvID in updatedIds) { // 检测这条会话数据是否存在,如果存在说明在后面的分页中,不存在则是被删除不处理 final isReal = await ImService.instance.getConversation(conversationID: cvID); if (isReal.success) { final V2TimConversation realConv = isReal.data; if (realConv.lastMessage != null) { // 陌生人会话列表单独处理 if (Get.isRegistered()) { logger.w('在陌生人会话列表'); final notifyCtl = Get.find(); // 有分组更新,没分组删除 if (realConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) { notifyCtl.updateLastMsg(conversation: realConv); } else { notifyCtl.del(conversation: realConv); } } // 同步更新nofriend会话菜单入口的未读数量 if (chatItem.isCustomAdmin?.contains(ConversationType.noFriend.name) ?? false) { await ctl.updateNoFriendMenu(); } if (realConv.conversationGroupList?.contains(ConversationType.noFriend.name) ?? false) { // 这个方法执行的逻辑:已有则刷新菜单入口数据,没有则创建菜单入口 await ctl.getNoFriendData(csion: realConv); } else { // 非陌生人会话,收集要插入的数据 (情况2)这条会话数据 willInsert.add(realConv); } } } } } } // 添加收集的数据,别忘了分页获取的去重 if (willInsert.isNotEmpty) { logger.w('收集到要插入的数据为:${willInsert.first.toLogString()}'); // willInsert 去重 final deduped = {for (var c in willInsert) c.conversationID.trim(): c}.values.toList(); // 已有会话ID集合 final existingIds = ctl.chatList.map((e) => e.conversation.conversationID.trim()).where((id) => id.isNotEmpty).toSet(); // 过滤掉已存在的会话 final filtered = deduped.where((c) => !existingIds.contains(c.conversationID.trim())).toList(); logger.w('最终需要插入的会话数量: ${filtered.length}, ids: ${filtered.map((c) => '"${(c.conversationID).trim()}"').toList()}'); final viewModelList = await ConversationViewModel.createConversationViewModel(convList: filtered); ctl.chatList.insertAll(0, viewModelList); } // 如果没当前会话列表为空 if (ctl.chatList.isEmpty) { // 重新获取一次 logger.w('重新获取会话'); ctl.initChatData(); ctl.getConversationList(); } //重新排序 ctl.chatList.sort((a, b) { final atime = a.conversation.lastMessage?.timestamp ?? 0; final btime = b.conversation.lastMessage?.timestamp ?? 0; return btime.compareTo(atime); // 降序 }); //去重 final seen = {}; ctl.chatList.retainWhere((item) { final id = item.conversation.conversationID.trim(); if (seen.contains(id)) { return false; } else { seen.add(id); return true; } }); ctl.chatList.refresh(); }, //changeEnd; ); // final ctl = Get.find(); // ctl.getConversationList(); initUnreadCount(); _addListener(); } // final rr = await ImService.instance.deleteConversationsFromGroup( // conversationIDList: [cov.conversationID], // groupName: 'noFriend', // ); // logger.w(rr.desc); /// 新建会话时候,根据消息的自定义属性给会话分组 void handleCoverstion(V2TimConversation cov) async { final message = cov.lastMessage; final isSelfSend = message!.isSelf; // 是否本人发送的消息 final typeEnum = conversationTypeFromString(message.cloudCustomData); // 会话类型 final needAdd = cov.conversationGroupList!.isEmpty == true; // 当前会话是否已加入了分组中 if (typeEnum != null && needAdd && isSelfSend == false) { logger.i('当前会话的类型要加入的组是:$typeEnum'); // 当前会话需要进行分组,检测 组 是否存在 final hasGroupRes = await ImService.instance.getConversationGroupList(); if (hasGroupRes.success) { final exists = hasGroupRes.data?.any((item) => item == typeEnum) ?? false; if (!exists) { // 组不存在,创建组并把会话加入group中 await ImService.instance.createConversationGroup( groupName: typeEnum, conversationIDList: ['c2c_${message.sender}'], ); logger.i('首次创建会话分组$typeEnum'); } else { // 分组存在直接添加 await ImService.instance.addConversationsToGroup( groupName: typeEnum, conversationIDList: ['c2c_${message.sender}'], ); logger.i('添加会话分组$typeEnum成功'); } if (typeEnum == ConversationType.noFriend.name) { //陌生人分组特殊处理 满足分组条件且已经有分组, final ctl = Get.find(); // 这个方法执行的逻辑:已有则刷新菜单入口数据,没有则创建菜单入口 ctl.getNoFriendData(csion: cov); } } } else { logger.w('新会话不需分组'); } } /// 初始化时获取一次未读总数 void initUnreadCount() async { final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getTotalUnreadMessageCount(); if (res.code == 0) { totalUnread.value = res.data ?? 0; Get.find().setBadge(TabType.chat, totalUnread.value); final to = res.data; logger.i('初始化未读消息数$to'); } else { //处理安卓端重新登录后获取未读数量失败的问题 logger.e('获取初始化未读数失败:${res.desc},重新补偿获取'); Future.delayed(Duration(seconds: 1), handAndroid); } } /// 处理安卓端异常的问题 handAndroid() { initUnreadCount(); final ctl = Get.find(); ctl.initChatData(); ctl.getConversationList(); } /// 添加会话未读数监听器 void _addListener() { TencentImSDKPlugin.v2TIMManager.getConversationManager().addConversationListener(listener: _listener); logger.i('未读数监听器注册成功'); } /// 手动更新total Future refreshUnreadCount() async { initUnreadCount(); } /// 移除监听器,防止重复注册 @override void onClose() { logger.i(_listener); logger.i('移除global未读监听器'); TencentImSDKPlugin.v2TIMManager.getConversationManager().removeConversationListener(listener: _listener); super.onClose(); } }