Browse Source
feat(message): 实现会话删除功能并优化UI交互
feat(message): 实现会话删除功能并优化UI交互
- 在ConversationController中新增deleteConversation方法,支持删除指定会话 - 在IMManager中实现deleteConversation底层逻辑,调用环信SDK删除会话 - 在会话列表页面添加侧滑删除功能,使用TDSwipeCell组件实现 -优化会话项布局和代码结构,提高可读性和维护性 - 更新依赖源为国内镜像地址,提升包加载速度 - 移除不必要的导入模块,精简代码体积 - 调整部分UI间距和样式,改善视觉效果ios
7 changed files with 626 additions and 255 deletions
Split View
Diff Options
-
22lib/controller/message/conversation_controller.dart
-
34lib/im/im_manager.dart
-
10lib/pages/home/home_page.dart
-
98lib/pages/message/conversation_tab.dart
-
278lib/rtc/rtm_manager.dart
-
438pubspec.lock
-
1pubspec.yaml
@ -0,0 +1,278 @@ |
|||
import 'package:agora_rtm/agora_rtm.dart'; |
|||
|
|||
/// 声网 RTM 管理器,聚合常用的客户端与 StreamChannel 操作 |
|||
class RTMManager { |
|||
RTMManager._internal(); |
|||
|
|||
static final RTMManager _instance = RTMManager._internal(); |
|||
factory RTMManager() => _instance; |
|||
static RTMManager get instance => _instance; |
|||
|
|||
RtmClient? _client; |
|||
bool _isInitialized = false; |
|||
bool _isLoggedIn = false; |
|||
String? _currentAppId; |
|||
String? _currentUserId; |
|||
|
|||
final Map<String, StreamChannel> _streamChannels = {}; |
|||
|
|||
/// 监听 link state |
|||
void Function(LinkStateEvent event)? onLinkStateEvent; |
|||
|
|||
/// 监听消息事件(频道 / 主题) |
|||
void Function(MessageEvent event)? onMessageEvent; |
|||
|
|||
/// 监听 presence 事件 |
|||
void Function(PresenceEvent event)? onPresenceEvent; |
|||
|
|||
/// 监听 topic 事件 |
|||
void Function(TopicEvent event)? onTopicEvent; |
|||
|
|||
/// 监听 lock 事件 |
|||
void Function(LockEvent event)? onLockEvent; |
|||
|
|||
/// 监听 storage 事件 |
|||
void Function(StorageEvent event)? onStorageEvent; |
|||
|
|||
/// 监听 token 事件 |
|||
void Function(TokenEvent event)? onTokenEvent; |
|||
|
|||
/// 统一错误通知 |
|||
void Function(RtmStatus status)? onOperationError; |
|||
|
|||
/// 创建/初始化 RTM Client |
|||
Future<bool> initialize({ |
|||
required String appId, |
|||
required String userId, |
|||
RtmConfig? config, |
|||
}) async { |
|||
if (_isInitialized && |
|||
_client != null && |
|||
_currentAppId == appId && |
|||
_currentUserId == userId) { |
|||
return true; |
|||
} |
|||
|
|||
await dispose(); |
|||
|
|||
final (status, client) = await RTM(appId, userId, config: config); |
|||
if (status.error) { |
|||
onOperationError?.call(status); |
|||
return false; |
|||
} |
|||
|
|||
_client = client; |
|||
_currentAppId = appId; |
|||
_currentUserId = userId; |
|||
_isInitialized = true; |
|||
_registerClientListeners(); |
|||
return true; |
|||
} |
|||
|
|||
/// 登录 RTM |
|||
Future<bool> login(String token) async { |
|||
_ensureInitialized(); |
|||
final (status, _) = await _client!.login(token); |
|||
final ok = _handleStatus(status); |
|||
if (ok) { |
|||
_isLoggedIn = true; |
|||
} |
|||
return ok; |
|||
} |
|||
|
|||
/// 登出 RTM |
|||
Future<void> logout() async { |
|||
if (!_isInitialized || _client == null || !_isLoggedIn) return; |
|||
|
|||
await leaveAllStreamChannels(); |
|||
final (status, _) = await _client!.logout(); |
|||
_handleStatus(status); |
|||
_isLoggedIn = false; |
|||
} |
|||
|
|||
/// 刷新 Token |
|||
Future<bool> renewToken(String token) async { |
|||
_ensureInitialized(); |
|||
final (status, _) = await _client!.renewToken(token); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 设置底层参数(JSON) |
|||
Future<bool> setParameters(String paramsJson) async { |
|||
_ensureInitialized(); |
|||
final status = await _client!.setParameters(paramsJson); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 发布频道文本消息 |
|||
Future<bool> publishChannelMessage({ |
|||
required String channelName, |
|||
required String message, |
|||
RtmChannelType channelType = RtmChannelType.message, |
|||
String? customType, |
|||
bool storeInHistory = false, |
|||
}) async { |
|||
_ensureInitialized(); |
|||
final (status, _) = await _client!.publish( |
|||
channelName, |
|||
message, |
|||
channelType: channelType, |
|||
customType: customType, |
|||
storeInHistory: storeInHistory, |
|||
); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 创建/加入 StreamChannel |
|||
Future<bool> joinStreamChannel( |
|||
String channelName, { |
|||
String? token, |
|||
bool withMetadata = false, |
|||
bool withPresence = true, |
|||
bool withLock = false, |
|||
bool beQuiet = false, |
|||
}) async { |
|||
_ensureInitialized(); |
|||
|
|||
StreamChannel? channel = _streamChannels[channelName]; |
|||
if (channel == null) { |
|||
final (createStatus, createdChannel) = await _client!.createStreamChannel( |
|||
channelName, |
|||
); |
|||
if (!_handleStatus(createStatus) || createdChannel == null) { |
|||
return false; |
|||
} |
|||
channel = createdChannel; |
|||
_streamChannels[channelName] = channel; |
|||
} |
|||
|
|||
final (status, _) = await channel.join( |
|||
token: token, |
|||
withMetadata: withMetadata, |
|||
withPresence: withPresence, |
|||
withLock: withLock, |
|||
beQuiet: beQuiet, |
|||
); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 离开 StreamChannel |
|||
Future<void> leaveStreamChannel(String channelName) async { |
|||
final channel = _streamChannels[channelName]; |
|||
if (channel == null) return; |
|||
|
|||
final (status, _) = await channel.leave(); |
|||
_handleStatus(status); |
|||
await channel.release(); |
|||
_streamChannels.remove(channelName); |
|||
} |
|||
|
|||
/// 离开全部 StreamChannel |
|||
Future<void> leaveAllStreamChannels() async { |
|||
final names = _streamChannels.keys.toList(); |
|||
for (final name in names) { |
|||
await leaveStreamChannel(name); |
|||
} |
|||
} |
|||
|
|||
/// 加入主题 |
|||
Future<bool> joinTopic({ |
|||
required String channelName, |
|||
required String topic, |
|||
RtmMessageQos qos = RtmMessageQos.unordered, |
|||
RtmMessagePriority priority = RtmMessagePriority.normal, |
|||
String meta = '', |
|||
bool syncWithMedia = false, |
|||
}) async { |
|||
final channel = await _requireChannel(channelName); |
|||
final (status, _) = await channel.joinTopic( |
|||
topic, |
|||
qos: qos, |
|||
priority: priority, |
|||
meta: meta, |
|||
syncWithMedia: syncWithMedia, |
|||
); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 发送主题消息 |
|||
Future<bool> publishTopicMessage({ |
|||
required String channelName, |
|||
required String topic, |
|||
required String message, |
|||
int sendTs = 0, |
|||
String? customType, |
|||
}) async { |
|||
final channel = await _requireChannel(channelName); |
|||
final (status, _) = await channel.publishTextMessage( |
|||
topic, |
|||
message, |
|||
sendTs: sendTs, |
|||
customType: customType, |
|||
); |
|||
return _handleStatus(status); |
|||
} |
|||
|
|||
/// 释放 RTM Client |
|||
Future<void> dispose() async { |
|||
await leaveAllStreamChannels(); |
|||
if (_client != null && _isLoggedIn) { |
|||
final (status, _) = await _client!.logout(); |
|||
_handleStatus(status); |
|||
} |
|||
await _client?.release(); |
|||
|
|||
_client = null; |
|||
_isInitialized = false; |
|||
_isLoggedIn = false; |
|||
_currentAppId = null; |
|||
_currentUserId = null; |
|||
_streamChannels.clear(); |
|||
} |
|||
|
|||
bool get isInitialized => _isInitialized; |
|||
bool get isLoggedIn => _isLoggedIn; |
|||
String? get currentUserId => _currentUserId; |
|||
Iterable<String> get joinedStreamChannels => _streamChannels.keys; |
|||
|
|||
void _registerClientListeners() { |
|||
if (_client == null) return; |
|||
_client!.addListener( |
|||
linkState: (event) => onLinkStateEvent?.call(event), |
|||
message: (event) => onMessageEvent?.call(event), |
|||
presence: (event) => onPresenceEvent?.call(event), |
|||
topic: (event) => onTopicEvent?.call(event), |
|||
lock: (event) => onLockEvent?.call(event), |
|||
storage: (event) => onStorageEvent?.call(event), |
|||
token: (event) => onTokenEvent?.call(event), |
|||
); |
|||
} |
|||
|
|||
Future<StreamChannel> _requireChannel(String channelName) async { |
|||
if (!_streamChannels.containsKey(channelName)) { |
|||
final ok = await joinStreamChannel(channelName); |
|||
if (!ok) { |
|||
throw Exception('加入 StreamChannel 失败:$channelName'); |
|||
} |
|||
} |
|||
final channel = _streamChannels[channelName]; |
|||
if (channel == null) { |
|||
throw Exception('StreamChannel 不存在:$channelName'); |
|||
} |
|||
return channel; |
|||
} |
|||
|
|||
bool _handleStatus(RtmStatus status) { |
|||
if (status.error) { |
|||
onOperationError?.call(status); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
void _ensureInitialized() { |
|||
if (!_isInitialized || _client == null) { |
|||
throw Exception('RTM Client 未初始化,请先调用 initialize'); |
|||
} |
|||
} |
|||
} |
|||
438
pubspec.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save