10 changed files with 1320 additions and 388 deletions
Unified 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
-
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