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.
330 lines
10 KiB
330 lines
10 KiB
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}');
|
|
}
|
|
}
|
|
}
|