11 changed files with 1419 additions and 483 deletions
Split View
Diff Options
-
66lib/controller/message/chat_controller.dart
-
330lib/im/chat_presence_manager.dart
-
227lib/im/im_manager.dart
-
4lib/model/home/marriage_data.dart
-
4lib/model/mine/user_base_data.dart
-
178lib/pages/message/chat_page.dart
-
194lib/widget/live/live_room_anchor_showcase.dart
-
38lib/widget/message/chat_input_bar.dart
-
676lib/widget/message/image_item.dart
-
66lib/widget/message/more_options_view.dart
-
119lib/widget/message/voice_item.dart
@ -0,0 +1,330 @@ |
|||
import 'package:im_flutter_sdk/im_flutter_sdk.dart'; |
|||
|
|||
/// 环信在线状态管理器 |
|||
class ChatPresenceManager { |
|||
// 单例模式 |
|||
static final ChatPresenceManager _instance = ChatPresenceManager._internal(); |
|||
factory ChatPresenceManager() => _instance; |
|||
ChatPresenceManager._internal(); |
|||
|
|||
// 用户ID到在线状态的映射 |
|||
final Map<String, bool> _onlineStatusMap = {}; |
|||
|
|||
// 用户ID到EMPresence的映射 |
|||
final Map<String, EMPresence> _presenceMap = {}; |
|||
|
|||
// 监听器ID集合(避免重复添加监听器) |
|||
final Set<String> _listenerIds = {}; |
|||
|
|||
// 在线状态变化回调 |
|||
final Map<String, Function(bool)> _statusCallbacks = {}; |
|||
|
|||
// 详细状态变化回调 |
|||
final Map<String, Function(EMPresence)> _presenceCallbacks = {}; |
|||
|
|||
/// 订阅用户在线状态 |
|||
/// [userId] 目标用户ID |
|||
/// [onStatusChanged] 状态变化回调(可选) |
|||
/// [onPresenceChanged] 详细状态变化回调(可选) |
|||
Future<void> subscribeUserPresence({ |
|||
required String userId, |
|||
Function(bool)? onStatusChanged, |
|||
Function(EMPresence)? onPresenceChanged, |
|||
}) async { |
|||
try { |
|||
// 存储回调 |
|||
if (onStatusChanged != null) { |
|||
_statusCallbacks[userId] = onStatusChanged; |
|||
} |
|||
if (onPresenceChanged != null) { |
|||
_presenceCallbacks[userId] = onPresenceChanged; |
|||
} |
|||
|
|||
// 订阅用户状态(7天有效期) |
|||
await EMClient.getInstance.presenceManager.subscribe( |
|||
members: [userId], |
|||
expiry: 604800, |
|||
); |
|||
|
|||
// 立即获取一次当前状态(会触发回调,更新UI) |
|||
await _fetchUserPresence(userId); |
|||
|
|||
// 设置监听器(如果尚未设置) |
|||
_setupPresenceListener(userId); |
|||
|
|||
print('✅ [ChatPresenceManager] 订阅成功: userId=$userId'); |
|||
} on EMError catch (e) { |
|||
print('订阅用户[$userId]在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 批量订阅多个用户的在线状态 |
|||
Future<void> subscribeUsersPresence({ |
|||
required List<String> userIds, |
|||
Function(String, bool)? onStatusChanged, |
|||
Function(String, EMPresence)? onPresenceChanged, |
|||
}) async { |
|||
if (userIds.isEmpty) return; |
|||
|
|||
try { |
|||
// 批量订阅(环信最多支持100个) |
|||
await EMClient.getInstance.presenceManager.subscribe( |
|||
members: userIds, |
|||
expiry: 604800, |
|||
); |
|||
|
|||
// 为每个用户设置回调 |
|||
for (final userId in userIds) { |
|||
if (onStatusChanged != null) { |
|||
_statusCallbacks[userId] = (isOnline) => onStatusChanged(userId, isOnline); |
|||
} |
|||
if (onPresenceChanged != null) { |
|||
_presenceCallbacks[userId] = (presence) => onPresenceChanged(userId, presence); |
|||
} |
|||
// 立即获取一次当前状态 |
|||
await _fetchUserPresence(userId); |
|||
} |
|||
|
|||
// 设置监听器 |
|||
_setupBatchPresenceListener(); |
|||
} on EMError catch (e) { |
|||
print('批量订阅用户在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 获取用户的在线状态 |
|||
bool isUserOnline(String userId) { |
|||
return _onlineStatusMap[userId] ?? false; |
|||
} |
|||
|
|||
/// 获取用户的详细在线状态信息 |
|||
EMPresence? getUserPresence(String userId) { |
|||
return _presenceMap[userId]; |
|||
} |
|||
|
|||
/// 获取在线状态文本描述 |
|||
String getStatusDescription(String userId) { |
|||
final presence = _presenceMap[userId]; |
|||
if (presence == null) return '未知'; |
|||
|
|||
final statusDesc = presence.statusDescription?.toLowerCase() ?? ''; |
|||
|
|||
// 根据标准状态返回 |
|||
if (statusDesc.contains('online')) return '在线'; |
|||
if (statusDesc.contains('away')) return '离开'; |
|||
if (statusDesc.contains('busy')) return '忙碌'; |
|||
if (statusDesc.contains('dnd')) return '勿扰'; |
|||
if (statusDesc.contains('offline')) return '离线'; |
|||
|
|||
return '未知'; |
|||
} |
|||
|
|||
/// 获取在线状态颜色 |
|||
int getStatusColor(String userId) { |
|||
final isOnline = _onlineStatusMap[userId] ?? false; |
|||
final presence = _presenceMap[userId]; |
|||
|
|||
if (!isOnline) return 0xFF999999; // 灰色 |
|||
|
|||
final statusDesc = presence?.statusDescription?.toLowerCase() ?? ''; |
|||
|
|||
if (statusDesc.contains('busy')) return 0xFFFF3B30; // 红色 |
|||
if (statusDesc.contains('away')) return 0xFFFF9500; // 橙色 |
|||
if (statusDesc.contains('dnd')) return 0xFFAF52DE; // 紫色 |
|||
if (statusDesc.contains('online')) return 0xFF34C759; // 绿色 |
|||
|
|||
return 0xFF34C759; // 默认绿色 |
|||
} |
|||
|
|||
/// 手动刷新用户在线状态 |
|||
Future<void> refreshUserPresence(String userId) async { |
|||
try { |
|||
await _fetchUserPresence(userId); |
|||
} on EMError catch (e) { |
|||
print('刷新用户[$userId]在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 取消订阅用户在线状态 |
|||
Future<void> unsubscribeUserPresence(String userId) async { |
|||
try { |
|||
// 取消订阅 |
|||
await EMClient.getInstance.presenceManager.unsubscribe(members: [userId]); |
|||
|
|||
// 清理回调 |
|||
_statusCallbacks.remove(userId); |
|||
_presenceCallbacks.remove(userId); |
|||
_onlineStatusMap.remove(userId); |
|||
_presenceMap.remove(userId); |
|||
} on EMError catch (e) { |
|||
print('取消订阅用户[$userId]在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 批量取消订阅 |
|||
Future<void> unsubscribeUsersPresence(List<String> userIds) async { |
|||
if (userIds.isEmpty) return; |
|||
|
|||
try { |
|||
await EMClient.getInstance.presenceManager.unsubscribe(members: userIds); |
|||
|
|||
for (final userId in userIds) { |
|||
_statusCallbacks.remove(userId); |
|||
_presenceCallbacks.remove(userId); |
|||
_onlineStatusMap.remove(userId); |
|||
_presenceMap.remove(userId); |
|||
} |
|||
} on EMError catch (e) { |
|||
print('批量取消订阅在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 清理所有订阅和监听器 |
|||
Future<void> dispose() async { |
|||
// 清理监听器 |
|||
for (final listenerId in _listenerIds) { |
|||
try { |
|||
EMClient.getInstance.presenceManager.removeEventHandler(listenerId); |
|||
} catch (e) { |
|||
// 忽略清理错误 |
|||
} |
|||
} |
|||
_listenerIds.clear(); |
|||
|
|||
// 清理回调 |
|||
_statusCallbacks.clear(); |
|||
_presenceCallbacks.clear(); |
|||
|
|||
// 清理数据 |
|||
_onlineStatusMap.clear(); |
|||
_presenceMap.clear(); |
|||
} |
|||
|
|||
/// 私有方法:获取用户在线状态 |
|||
Future<void> _fetchUserPresence(String userId) async { |
|||
try { |
|||
List<EMPresence> presences = await EMClient.getInstance.presenceManager |
|||
.fetchPresenceStatus(members: [userId]); |
|||
|
|||
if (presences.isNotEmpty) { |
|||
final presence = presences.first; |
|||
print('📥 [ChatPresenceManager] 获取到Presence状态: userId=$userId, statusDescription=${presence.statusDescription}'); |
|||
_presenceMap[userId] = presence; |
|||
_updateOnlineStatus(userId, presence); |
|||
} else { |
|||
print('⚠️ [ChatPresenceManager] 未获取到Presence状态: userId=$userId'); |
|||
} |
|||
} on EMError catch (e) { |
|||
print('获取用户[$userId]在线状态失败: ${e.code}, ${e.description}'); |
|||
rethrow; |
|||
} |
|||
} |
|||
|
|||
/// 私有方法:更新在线状态 |
|||
void _updateOnlineStatus(String userId, EMPresence presence) { |
|||
final statusDesc = presence.statusDescription?.toLowerCase() ?? ''; |
|||
final oldStatus = _onlineStatusMap[userId] ?? false; |
|||
bool newStatus; |
|||
|
|||
// 判断在线状态逻辑 |
|||
if (statusDesc.contains('online') || statusDesc.contains('available')) { |
|||
newStatus = true; |
|||
} else if (statusDesc.contains('offline')) { |
|||
newStatus = false; |
|||
} else { |
|||
// away、busy、dnd 等状态,默认算"在线"(可根据业务需求调整) |
|||
newStatus = true; |
|||
} |
|||
|
|||
// 检查是否是首次设置状态 |
|||
final isFirstTime = !_onlineStatusMap.containsKey(userId); |
|||
|
|||
// 更新状态 |
|||
_onlineStatusMap[userId] = newStatus; |
|||
|
|||
// 打印调试信息 |
|||
print('🔄 [ChatPresenceManager] 更新在线状态: userId=$userId, statusDescription=$statusDesc, oldStatus=$oldStatus, newStatus=$newStatus, isFirstTime=$isFirstTime'); |
|||
|
|||
// 触发回调(如果状态发生变化,或者首次设置) |
|||
if (isFirstTime || oldStatus != newStatus) { |
|||
print('✅ [ChatPresenceManager] 触发状态回调: userId=$userId, isOnline=$newStatus'); |
|||
_statusCallbacks[userId]?.call(newStatus); |
|||
} |
|||
// 始终触发详细回调 |
|||
_presenceCallbacks[userId]?.call(presence); |
|||
} |
|||
|
|||
/// 私有方法:设置在线状态监听器 |
|||
void _setupPresenceListener(String userId) { |
|||
final listenerId = 'presence_$userId'; |
|||
if (_listenerIds.contains(listenerId)) return; |
|||
|
|||
EMClient.getInstance.presenceManager.addEventHandler( |
|||
listenerId, |
|||
EMPresenceEventHandler( |
|||
onPresenceStatusChanged: (list) { |
|||
print('📡 [ChatPresenceManager] 收到Presence状态变化通知: userId=$userId, 变化数量=${list.length}'); |
|||
for (var presence in list) { |
|||
if (presence.publisher == userId) { |
|||
print('✅ [ChatPresenceManager] 匹配到目标用户: userId=$userId'); |
|||
_presenceMap[userId] = presence; |
|||
_updateOnlineStatus(userId, presence); |
|||
break; |
|||
} |
|||
} |
|||
}, |
|||
), |
|||
); |
|||
|
|||
_listenerIds.add(listenerId); |
|||
} |
|||
|
|||
/// 私有方法:设置批量监听器 |
|||
void _setupBatchPresenceListener() { |
|||
const listenerId = 'presence_batch_listener'; |
|||
if (_listenerIds.contains(listenerId)) return; |
|||
|
|||
EMClient.getInstance.presenceManager.addEventHandler( |
|||
listenerId, |
|||
EMPresenceEventHandler( |
|||
onPresenceStatusChanged: (list) { |
|||
for (var presence in list) { |
|||
final userId = presence.publisher; |
|||
if (userId != null && _statusCallbacks.containsKey(userId)) { |
|||
_presenceMap[userId] = presence; |
|||
_updateOnlineStatus(userId, presence); |
|||
} |
|||
} |
|||
}, |
|||
), |
|||
); |
|||
|
|||
_listenerIds.add(listenerId); |
|||
} |
|||
|
|||
/// 私有方法:刷新所有已订阅用户的状态 |
|||
Future<void> _refreshAllSubscribedUsers() async { |
|||
final userIds = _statusCallbacks.keys.toList(); |
|||
if (userIds.isEmpty) return; |
|||
|
|||
try { |
|||
List<EMPresence> presences = await EMClient.getInstance.presenceManager |
|||
.fetchPresenceStatus(members: userIds); |
|||
|
|||
for (var presence in presences) { |
|||
final publisherId = presence.publisher; |
|||
if (publisherId != null && publisherId.isNotEmpty) { |
|||
_presenceMap[publisherId] = presence; |
|||
_updateOnlineStatus(publisherId, presence); |
|||
} |
|||
} |
|||
} on EMError catch (e) { |
|||
print('刷新所有订阅用户状态失败: ${e.code}, ${e.description}'); |
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save