Jolie 3 months ago
parent
commit
ef5bc4bf5e
3 changed files with 215 additions and 16 deletions
  1. 139
      lib/controller/message/conversation_controller.dart
  2. 45
      lib/pages/message/conversation_tab.dart
  3. 47
      lib/pages/message/message_page.dart

139
lib/controller/message/conversation_controller.dart

@ -35,6 +35,14 @@ class ExtendedUserInfo {
}
}
//
enum FilterType {
none, //
lastChatTime, //
unread, //
online, // 线
}
class ConversationController extends GetxController {
//
final conversations = <EMConversation>[].obs;
@ -46,6 +54,9 @@ class ConversationController extends GetxController {
// userId -> ExtendedUserInfo
final Map<String, ExtendedUserInfo> _userInfoCache = {};
//
final filterType = FilterType.none.obs;
/// ChatController
void cacheUserInfo(String userId, ExtendedUserInfo userInfo) {
_userInfoCache[userId] = userInfo;
@ -591,4 +602,132 @@ class ConversationController extends GetxController {
return false;
}
}
///
void setFilterType(FilterType type) {
filterType.value = type;
if (Get.isLogEnable) {
Get.log('✅ [ConversationController] 设置筛选类型: $type');
}
}
///
Future<List<EMConversation>> getFilteredConversations() async {
List<EMConversation> filteredList = List.from(conversations);
switch (filterType.value) {
case FilterType.none:
//
break;
case FilterType.lastChatTime:
//
//
final List<MapEntry<EMConversation, int>> conversationTimes = await Future.wait(
filteredList.map((conv) async {
final message = await lastMessage(conv);
final time = message?.serverTime ?? 0;
return MapEntry(conv, time);
}),
);
//
conversationTimes.sort((a, b) => b.value.compareTo(a.value));
filteredList = conversationTimes.map((entry) => entry.key).toList();
break;
case FilterType.unread:
//
final List<MapEntry<EMConversation, int>> conversationUnreads = await Future.wait(
filteredList.map((conv) async {
final unreadCount = await getUnreadCount(conv);
return MapEntry(conv, unreadCount);
}),
);
//
final unreadConversations = conversationUnreads
.where((entry) => entry.value > 0)
.toList();
//
unreadConversations.sort((a, b) => b.value.compareTo(a.value));
filteredList = unreadConversations.map((entry) => entry.key).toList();
break;
case FilterType.online:
// 线
final List<EMConversation?> onlineConversations = await Future.wait(
filteredList.map((conv) async {
final isOnline = await _checkUserOnline(conv);
return isOnline ? conv : null;
}),
);
filteredList = onlineConversations
.whereType<EMConversation>()
.toList();
break;
}
return filteredList;
}
/// 线
Future<bool> _checkUserOnline(EMConversation conversation) async {
try {
//
final message = await lastMessage(conversation);
if (message == null) {
return false;
}
// 线
Map<String, dynamic>? attributes;
try {
attributes = message.attributes;
} catch (e) {
return false;
}
if (attributes == null || attributes.isEmpty) {
return false;
}
// 线
// sender_isOnline
// receiver_isOnline
String? isOnlineStr;
String? lastActiveTimeStr;
if (message.direction == MessageDirection.RECEIVE) {
isOnlineStr = attributes['sender_isOnline'] as String?;
lastActiveTimeStr = attributes['sender_lastActiveTime'] as String?;
} else {
isOnlineStr = attributes['receiver_isOnline'] as String?;
lastActiveTimeStr = attributes['receiver_lastActiveTime'] as String?;
}
if (isOnlineStr == 'true') {
// 5线
if (lastActiveTimeStr != null) {
try {
final lastActiveTime = int.parse(lastActiveTimeStr);
final now = DateTime.now().millisecondsSinceEpoch;
final diff = now - lastActiveTime;
// 5线5 * 60 * 1000
return diff < 5 * 60 * 1000;
} catch (e) {
// 使 isOnline
return true;
}
} else {
return true;
}
}
return false;
} catch (e) {
if (Get.isLogEnable) {
Get.log('⚠️ [ConversationController] 检查用户在线状态失败: $e');
}
return false;
}
}
}

45
lib/pages/message/conversation_tab.dart

@ -44,14 +44,43 @@ class _ConversationTabState extends State<ConversationTab>
);
}
return ListView.builder(
padding: const EdgeInsets.only(top: 8),
itemCount: controller.conversations.length,
itemBuilder: (context, index) {
final conversation = controller.conversations[index];
return _buildConversationItem(conversation);
},
);
//
// 使 Obx FutureBuilder
return Obx(() {
return FutureBuilder<List<EMConversation>>(
future: controller.getFilteredConversations(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final filteredConversations = snapshot.data ?? [];
if (filteredConversations.isEmpty) {
return Center(
child: Text(
controller.filterType.value == FilterType.none
? '暂无会话'
: '暂无符合条件的会话',
style: const TextStyle(
fontSize: 14,
color: Colors.grey,
),
),
);
}
return ListView.builder(
padding: const EdgeInsets.only(top: 8),
itemCount: filteredConversations.length,
itemBuilder: (context, index) {
final conversation = filteredConversations[index];
return _buildConversationItem(conversation);
},
);
},
);
});
}),
),
],

47
lib/pages/message/message_page.dart

@ -144,6 +144,8 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
overlay.size.height - (buttonPosition.dy + buttonSize.height + 4), // bottom
);
final ConversationController conversationController = Get.find<ConversationController>();
showMenu(
context: context,
position: position,
@ -158,9 +160,15 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
icon: Assets.imagesLastMsgIcon,
text: '最后聊天时间',
showDivider: true,
isSelected: conversationController.filterType.value == FilterType.lastChatTime,
onTap: () {
Navigator.pop(context);
// TODO:
//
if (conversationController.filterType.value == FilterType.lastChatTime) {
conversationController.setFilterType(FilterType.none);
} else {
conversationController.setFilterType(FilterType.lastChatTime);
}
},
),
),
@ -170,9 +178,15 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
icon: Assets.imagesUnreadIcon,
text: '未读消息',
showDivider: true,
isSelected: conversationController.filterType.value == FilterType.unread,
onTap: () {
Navigator.pop(context);
// TODO:
//
if (conversationController.filterType.value == FilterType.unread) {
conversationController.setFilterType(FilterType.none);
} else {
conversationController.setFilterType(FilterType.unread);
}
},
),
),
@ -182,9 +196,15 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
icon: Assets.imagesOnlineMsgIcon,
text: '当前在线',
showDivider: false,
isSelected: conversationController.filterType.value == FilterType.online,
onTap: () {
Navigator.pop(context);
// TODO: 线
//
if (conversationController.filterType.value == FilterType.online) {
conversationController.setFilterType(FilterType.none);
} else {
conversationController.setFilterType(FilterType.online);
}
},
),
),
@ -198,6 +218,7 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
required String text,
required bool showDivider,
required VoidCallback onTap,
bool isSelected = false,
}) {
return InkWell(
onTap: onTap,
@ -206,6 +227,7 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
color: isSelected ? Colors.blue.withOpacity(0.1) : Colors.transparent,
child: Row(
children: [
Image.asset(
@ -214,13 +236,22 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
height: 20,
),
const SizedBox(width: 12),
Text(
text,
style: const TextStyle(
fontSize: 14,
color: Colors.black,
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 14,
color: isSelected ? Colors.blue : Colors.black,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
),
),
),
if (isSelected)
const Icon(
Icons.check,
size: 18,
color: Colors.blue,
),
],
),
),

Loading…
Cancel
Save