退出房间删除rtc, 消息列表判断
This commit is contained in:
parent
15ac93c737
commit
a5bc029113
@ -78,10 +78,7 @@ class SCGlobalConfig {
|
|||||||
|
|
||||||
static Set<String> get systemConversationIds {
|
static Set<String> get systemConversationIds {
|
||||||
return {
|
return {
|
||||||
"administrator",
|
for (final userId in systemUserIds) ...{userId, "c2c_$userId"},
|
||||||
...systemUserIds
|
|
||||||
.where((element) => element != "administrator")
|
|
||||||
.map((element) => "c2c_$element"),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +90,7 @@ class SCGlobalConfig {
|
|||||||
if (conversationId == null || conversationId.isEmpty) {
|
if (conversationId == null || conversationId.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return systemConversationIds.contains(conversationId);
|
return systemUserIds.contains(_normalizeConversationUserId(conversationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isSystemUserId(String? userId) {
|
static bool isSystemUserId(String? userId) {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import 'package:marquee/marquee.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
|
||||||
@ -51,7 +52,6 @@ import 'package:yumi/shared/tools/sc_keybord_util.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_message_notifier.dart';
|
import 'package:yumi/shared/tools/sc_message_notifier.dart';
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/business_logic/usecases/custom_tab_selector.dart';
|
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/modules/index/main_route.dart';
|
import 'package:yumi/modules/index/main_route.dart';
|
||||||
|
|
||||||
@ -104,6 +104,34 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
List<String> coinsTitles = ["100", "1000", "10000", "50000"];
|
List<String> coinsTitles = ["100", "1000", "10000", "50000"];
|
||||||
String selecteCoins = "100";
|
String selecteCoins = "100";
|
||||||
|
|
||||||
|
bool get _isSystemConversation =>
|
||||||
|
SCGlobalConfig.isSystemConversationId(
|
||||||
|
currentConversation?.conversationID,
|
||||||
|
) ||
|
||||||
|
SCGlobalConfig.isSystemUserId(currentConversation?.userID);
|
||||||
|
|
||||||
|
bool get _isC2CConversation =>
|
||||||
|
currentConversation?.type == ConversationType.V2TIM_C2C;
|
||||||
|
|
||||||
|
bool get _isGroupConversation =>
|
||||||
|
currentConversation?.type == ConversationType.V2TIM_GROUP;
|
||||||
|
|
||||||
|
bool get _canOpenFriendProfile => _isC2CConversation && friend != null;
|
||||||
|
|
||||||
|
bool get _canSendConversationMessage =>
|
||||||
|
_isC2CConversation && !_isSystemConversation;
|
||||||
|
|
||||||
|
String get _conversationTitle {
|
||||||
|
final friendNickname = friend?.userNickname ?? "";
|
||||||
|
if (friendNickname.isNotEmpty) {
|
||||||
|
return friendNickname;
|
||||||
|
}
|
||||||
|
return currentConversation?.showName ??
|
||||||
|
currentConversation?.userID ??
|
||||||
|
currentConversation?.groupID ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -119,7 +147,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
|
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
|
||||||
loadMsg();
|
loadMsg();
|
||||||
|
|
||||||
if (friend == null) {
|
if (_isC2CConversation && friend == null) {
|
||||||
loadFriend();
|
loadFriend();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,18 +248,17 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
alignment: AlignmentDirectional.center,
|
alignment: AlignmentDirectional.center,
|
||||||
child: socialchatNickNameText(
|
child: socialchatNickNameText(
|
||||||
friend?.userNickname ?? "",
|
_conversationTitle,
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
type: "",
|
type: "",
|
||||||
needScroll:
|
needScroll:
|
||||||
(friend?.userNickname?.characters.length ??
|
_conversationTitle.characters.length > 22,
|
||||||
0) >
|
|
||||||
22,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (_canOpenFriendProfile)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -262,10 +289,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
top: false,
|
top: false,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (!SCGlobalConfig.isSystemConversationId(
|
if (_canSendConversationMessage) _input(),
|
||||||
currentConversation?.conversationID,
|
|
||||||
))
|
|
||||||
_input(),
|
|
||||||
_tools(provider),
|
_tools(provider),
|
||||||
_emoji(),
|
_emoji(),
|
||||||
// _fahongbao(),
|
// _fahongbao(),
|
||||||
@ -285,18 +309,18 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadFriend() {
|
void loadFriend() {
|
||||||
if (SCGlobalConfig.isSystemConversationId(
|
final userId = currentConversation?.userID;
|
||||||
currentConversation?.conversationID,
|
if (!_isC2CConversation ||
|
||||||
) ||
|
_isSystemConversation ||
|
||||||
SCGlobalConfig.isSystemUserId(currentConversation?.userID)) {
|
SCGlobalConfig.isSystemUserId(userId) ||
|
||||||
|
userId == null ||
|
||||||
|
userId.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SCLoadingManager.show();
|
SCLoadingManager.show();
|
||||||
Future.wait([
|
Future.wait([
|
||||||
SCAccountRepository().loadUserInfo("${currentConversation?.userID}"),
|
SCAccountRepository().loadUserInfo(userId),
|
||||||
SCAccountRepository().friendRelationCheck(
|
SCAccountRepository().friendRelationCheck(userId),
|
||||||
"${currentConversation?.userID}",
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
.then((result) {
|
.then((result) {
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
@ -318,19 +342,10 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
onLoading: () async {
|
onLoading: () async {
|
||||||
print('onLoading');
|
print('onLoading');
|
||||||
if (currentConversationMessageList.isNotEmpty) {
|
if (currentConversationMessageList.isNotEmpty) {
|
||||||
// 拉取单聊历史消息
|
final messages = await _loadHistoryMessages(
|
||||||
// 首次拉取,lastMsgID 设置为 null
|
|
||||||
// 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的id
|
|
||||||
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
|
|
||||||
await TencentImSDKPlugin.v2TIMManager
|
|
||||||
.getMessageManager()
|
|
||||||
.getC2CHistoryMessageList(
|
|
||||||
userID: currentConversation!.userID ?? "",
|
|
||||||
count: 30,
|
count: 30,
|
||||||
lastMsgID: currentConversationMessageList.last.msgID,
|
lastMsgID: currentConversationMessageList.last.msgID,
|
||||||
);
|
);
|
||||||
List<V2TimMessage> messages =
|
|
||||||
v2timValueCallback.data as List<V2TimMessage>;
|
|
||||||
print('加载前:${currentConversationMessageList.length}');
|
print('加载前:${currentConversationMessageList.length}');
|
||||||
currentConversationMessageList.addAll(messages);
|
currentConversationMessageList.addAll(messages);
|
||||||
print('加载后:${currentConversationMessageList.length}');
|
print('加载后:${currentConversationMessageList.length}');
|
||||||
@ -386,6 +401,54 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<V2TimMessage>> _loadHistoryMessages({
|
||||||
|
required int count,
|
||||||
|
String? lastMsgID,
|
||||||
|
}) async {
|
||||||
|
final messageManager = TencentImSDKPlugin.v2TIMManager.getMessageManager();
|
||||||
|
if (_isGroupConversation) {
|
||||||
|
final groupId = currentConversation?.groupID;
|
||||||
|
if (groupId == null || groupId.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
final result = await messageManager.getGroupHistoryMessageList(
|
||||||
|
groupID: groupId,
|
||||||
|
count: count,
|
||||||
|
lastMsgID: lastMsgID,
|
||||||
|
);
|
||||||
|
return result.data ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final userId = currentConversation?.userID;
|
||||||
|
if (userId == null || userId.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
final result = await messageManager.getC2CHistoryMessageList(
|
||||||
|
userID: userId,
|
||||||
|
count: count,
|
||||||
|
lastMsgID: lastMsgID,
|
||||||
|
);
|
||||||
|
return result.data ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _markCurrentConversationAsRead() async {
|
||||||
|
final messageManager = TencentImSDKPlugin.v2TIMManager.getMessageManager();
|
||||||
|
if (_isGroupConversation) {
|
||||||
|
final groupId = currentConversation?.groupID;
|
||||||
|
if (groupId == null || groupId.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await messageManager.markGroupMessageAsRead(groupID: groupId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final userId = currentConversation?.userID;
|
||||||
|
if (userId == null || userId.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await messageManager.markC2CMessageAsRead(userID: userId);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadMsg() async {
|
Future<void> loadMsg() async {
|
||||||
// 1. 在初始化时,添加高级消息监听器
|
// 1. 在初始化时,添加高级消息监听器
|
||||||
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(
|
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(
|
||||||
@ -404,19 +467,9 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
|
final messages = await _loadHistoryMessages(count: 100);
|
||||||
await TencentImSDKPlugin.v2TIMManager
|
print("messages : ${messages.length}");
|
||||||
.getMessageManager()
|
currentConversationMessageList.clear();
|
||||||
.getC2CHistoryMessageList(
|
|
||||||
userID: currentConversation!.userID!,
|
|
||||||
count: 100,
|
|
||||||
lastMsgID: null,
|
|
||||||
);
|
|
||||||
List<V2TimMessage> messages = v2timValueCallback.data!;
|
|
||||||
// List<V2TimMessage> messages = await FTIM.getMessageManager().getMessages(conversation: currentConversation);
|
|
||||||
print("messages : ${messages?.length ?? 0}");
|
|
||||||
currentConversationMessageList ??= [];
|
|
||||||
currentConversationMessageList?.clear();
|
|
||||||
|
|
||||||
for (var msg in messages) {
|
for (var msg in messages) {
|
||||||
if (!msg.isSelf! && msg.isPeerRead!) {
|
if (!msg.isSelf! && msg.isPeerRead!) {
|
||||||
@ -425,7 +478,8 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
.sendMessageReadReceipts(messageIDList: [msg.msgID!]);
|
.sendMessageReadReceipts(messageIDList: [msg.msgID!]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentConversationMessageList.insertAll(0, messages ?? []);
|
currentConversationMessageList.insertAll(0, messages);
|
||||||
|
await _markCurrentConversationAsRead();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -767,7 +821,11 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
if (message == null) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.userID != currentConversation?.userID) return;
|
if (_isGroupConversation) {
|
||||||
|
if (message.groupID != currentConversation?.groupID) return;
|
||||||
|
} else if (message.userID != currentConversation?.userID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int? index;
|
int? index;
|
||||||
for (var element in currentConversationMessageList) {
|
for (var element in currentConversationMessageList) {
|
||||||
@ -788,14 +846,12 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
currentConversationMessageList.removeAt(index!);
|
currentConversationMessageList.removeAt(index);
|
||||||
currentConversationMessageList.insert(index!, message);
|
currentConversationMessageList.insert(index, message);
|
||||||
} else {
|
} else {
|
||||||
currentConversationMessageList.insert(0, message);
|
currentConversationMessageList.insert(0, message);
|
||||||
}
|
}
|
||||||
await TencentImSDKPlugin.v2TIMManager
|
await _markCurrentConversationAsRead();
|
||||||
.getMessageManager()
|
|
||||||
.markC2CMessageAsRead(userID: currentConversation!.userID!);
|
|
||||||
//发送已读回执
|
//发送已读回执
|
||||||
await TencentImSDKPlugin.v2TIMManager
|
await TencentImSDKPlugin.v2TIMManager
|
||||||
.getMessageManager()
|
.getMessageManager()
|
||||||
@ -849,7 +905,7 @@ class _MessageItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
String time = SCMDateUtils.formatMessageTime(
|
String time = SCMDateUtils.formatMessageTime(
|
||||||
context,
|
context,
|
||||||
DateTime.fromMillisecondsSinceEpoch(timestamp ?? 0),
|
DateTime.fromMillisecondsSinceEpoch(timestamp),
|
||||||
);
|
);
|
||||||
if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) {
|
if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) {
|
||||||
return Container(
|
return Container(
|
||||||
|
|||||||
@ -572,6 +572,26 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _bootstrapRoomHeartbeatState() async {
|
||||||
|
final String roomId =
|
||||||
|
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (roomId.isEmpty) {
|
||||||
|
_resetHeartbeatTracking();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await SCHeartbeatUtils.scheduleHeartbeat(
|
||||||
|
SCHeartbeatStatus.VOICE_LIVE.name,
|
||||||
|
false,
|
||||||
|
roomId: roomId,
|
||||||
|
);
|
||||||
|
_lastScheduledVoiceLiveRoomId = roomId;
|
||||||
|
_lastScheduledVoiceLiveOnMic = false;
|
||||||
|
if (_lastScheduledAnchorRoomId != null) {
|
||||||
|
SCHeartbeatUtils.cancelAnchorTimer();
|
||||||
|
_lastScheduledAnchorRoomId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _applyLocalAudioRuntimeState({
|
void _applyLocalAudioRuntimeState({
|
||||||
required ClientRoleType clientRole,
|
required ClientRoleType clientRole,
|
||||||
required bool muted,
|
required bool muted,
|
||||||
@ -1058,6 +1078,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _bootstrapRoomHeartbeatState();
|
||||||
|
|
||||||
///获取麦位
|
///获取麦位
|
||||||
retrieveMicrophoneList();
|
retrieveMicrophoneList();
|
||||||
fetchOnlineUsersList();
|
fetchOnlineUsersList();
|
||||||
|
|||||||
@ -133,22 +133,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
///客服
|
///客服
|
||||||
SocialChatUserProfile? customerInfo;
|
SocialChatUserProfile? customerInfo;
|
||||||
|
|
||||||
|
bool _isSystemConversation(V2TimConversation? conversation) {
|
||||||
|
if (conversation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return SCGlobalConfig.isSystemConversationId(conversation.conversationID) ||
|
||||||
|
SCGlobalConfig.isSystemUserId(conversation.userID);
|
||||||
|
}
|
||||||
|
|
||||||
int _systemUnreadCount() {
|
int _systemUnreadCount() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
for (final conversation in conversationMap.values) {
|
||||||
count += conversationMap[conversationId]?.unreadCount ?? 0;
|
if (_isSystemConversation(conversation)) {
|
||||||
|
count += conversation.unreadCount ?? 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
V2TimConversation getPreferredSystemConversation() {
|
V2TimConversation getPreferredSystemConversation() {
|
||||||
final systemConversations =
|
final systemConversations =
|
||||||
conversationMap.values
|
conversationMap.values.where(_isSystemConversation).toList()
|
||||||
.where(
|
|
||||||
(element) =>
|
|
||||||
SCGlobalConfig.isSystemConversationId(element.conversationID),
|
|
||||||
)
|
|
||||||
.toList()
|
|
||||||
..sort((e1, e2) {
|
..sort((e1, e2) {
|
||||||
final time1 = e1.lastMessage?.timestamp ?? 0;
|
final time1 = e1.lastMessage?.timestamp ?? 0;
|
||||||
final time2 = e2.lastMessage?.timestamp ?? 0;
|
final time2 = e2.lastMessage?.timestamp ?? 0;
|
||||||
@ -169,6 +174,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
void getConversationList() {
|
void getConversationList() {
|
||||||
List<V2TimConversation> list = conversationMap.values.toList();
|
List<V2TimConversation> list = conversationMap.values.toList();
|
||||||
list.removeWhere((element) {
|
list.removeWhere((element) {
|
||||||
|
if (_isSystemConversation(element)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (element.conversationID == "c2c_${customerInfo?.id}") {
|
if (element.conversationID == "c2c_${customerInfo?.id}") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -501,7 +509,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
|
|
||||||
_onNew1v1Message(V2TimMessage? message, {String? msgId}) async {
|
_onNew1v1Message(V2TimMessage? message, {String? msgId}) async {
|
||||||
for (var element in conversationList) {
|
for (var element in conversationList) {
|
||||||
if (message?.userID == element?.userID) {
|
if (message?.userID == element.userID) {
|
||||||
element.lastMessage = message;
|
element.lastMessage = message;
|
||||||
if (onNewMessageCurrentConversationListener == null) {
|
if (onNewMessageCurrentConversationListener == null) {
|
||||||
element.unreadCount = element.unreadCount! + 1;
|
element.unreadCount = element.unreadCount! + 1;
|
||||||
@ -1740,7 +1748,14 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future quitGroup(String groupID) async {
|
Future quitGroup(String groupID) async {
|
||||||
|
if (groupID.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID);
|
await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID);
|
||||||
|
} finally {
|
||||||
|
await deleteConversationById("group_$groupID");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///清屏
|
///清屏
|
||||||
@ -1857,8 +1872,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateSystemCount(int count) {
|
void updateSystemCount(int count) {
|
||||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
for (final conversation in conversationMap.values) {
|
||||||
conversationMap[conversationId]?.unreadCount = 0;
|
if (_isSystemConversation(conversation)) {
|
||||||
|
conversation.unreadCount = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
systemUnReadCount = 0;
|
systemUnReadCount = 0;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -1870,21 +1887,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearC2CHistoryMessage(String conversationID, bool needShowToast) async {
|
Future<void> deleteConversationById(
|
||||||
// 清空单聊本地及云端的消息(不删除会话)
|
String conversationID, {
|
||||||
|
bool needShowToast = false,
|
||||||
V2TimCallback clearC2CHistoryMessageRes = await TencentImSDKPlugin
|
}) async {
|
||||||
.v2TIMManager
|
if (conversationID.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
V2TimCallback deleteConversationRes = await TencentImSDKPlugin.v2TIMManager
|
||||||
.getConversationManager()
|
.getConversationManager()
|
||||||
.deleteConversation(conversationID: conversationID); // 需要清空记录的用户id
|
.deleteConversation(conversationID: conversationID);
|
||||||
if (clearC2CHistoryMessageRes.code == 0) {
|
if (deleteConversationRes.code == 0 && needShowToast && context != null) {
|
||||||
// 清除成功
|
|
||||||
if (needShowToast) {
|
|
||||||
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
|
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
|
||||||
}
|
}
|
||||||
initConversation();
|
await initConversation();
|
||||||
} else {
|
|
||||||
// 清除失败,可以查看 clearC2CHistoryMessageRes.desc 获取错误描述
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearC2CHistoryMessage(String conversationID, bool needShowToast) async {
|
||||||
|
await deleteConversationById(conversationID, needShowToast: needShowToast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class SCHeartbeatUtils {
|
|||||||
static bool _u = false;
|
static bool _u = false;
|
||||||
|
|
||||||
///定时发送心跳
|
///定时发送心跳
|
||||||
static void scheduleHeartbeat(
|
static Future<void> scheduleHeartbeat(
|
||||||
String status,
|
String status,
|
||||||
bool upMick, {
|
bool upMick, {
|
||||||
String? roomId,
|
String? roomId,
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -296,6 +295,39 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
V2TimConversation get conversation => widget.conversation;
|
V2TimConversation get conversation => widget.conversation;
|
||||||
SocialChatUserProfile? user;
|
SocialChatUserProfile? user;
|
||||||
|
|
||||||
|
bool get _isSystemConversation =>
|
||||||
|
SCGlobalConfig.isSystemConversationId(conversation.conversationID) ||
|
||||||
|
SCGlobalConfig.isSystemUserId(conversation.userID);
|
||||||
|
|
||||||
|
bool get _isC2CConversation =>
|
||||||
|
conversation.type == ConversationType.V2TIM_C2C;
|
||||||
|
|
||||||
|
bool get _shouldLoadUserInfo =>
|
||||||
|
_isC2CConversation &&
|
||||||
|
!_isSystemConversation &&
|
||||||
|
conversation.conversationID != "customer" &&
|
||||||
|
conversation.conversationID != "article" &&
|
||||||
|
(conversation.userID?.isNotEmpty ?? false);
|
||||||
|
|
||||||
|
String get _displayAvatar {
|
||||||
|
final userAvatar = user?.userAvatar ?? "";
|
||||||
|
if (userAvatar.isNotEmpty) {
|
||||||
|
return userAvatar;
|
||||||
|
}
|
||||||
|
return conversation.faceUrl ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String get _displayName {
|
||||||
|
final nickname = user?.userNickname ?? "";
|
||||||
|
if (nickname.isNotEmpty) {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
return conversation.showName ??
|
||||||
|
conversation.userID ??
|
||||||
|
conversation.groupID ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
loadUserInfo();
|
loadUserInfo();
|
||||||
@ -351,7 +383,13 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
// print('time :${Platform.isAndroid ? (conversation.lastMsg?.timestamp ?? 0) * 1000 : conversation.lastMsg?.timestamp}');
|
// print('time :${Platform.isAndroid ? (conversation.lastMsg?.timestamp ?? 0) * 1000 : conversation.lastMsg?.timestamp}');
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (conversation != null) {
|
if (_isC2CConversation && (conversation.userID?.isEmpty ?? true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (conversation.type == ConversationType.V2TIM_GROUP &&
|
||||||
|
(conversation.groupID?.isEmpty ?? true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
conversation.unreadCount = 0;
|
conversation.unreadCount = 0;
|
||||||
var bool = await Provider.of<RtmProvider>(
|
var bool = await Provider.of<RtmProvider>(
|
||||||
context,
|
context,
|
||||||
@ -360,14 +398,11 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
if (!bool) return;
|
if (!bool) return;
|
||||||
var json = jsonEncode(widget.conversation.toJson());
|
var json = jsonEncode(widget.conversation.toJson());
|
||||||
final route =
|
final route =
|
||||||
SCGlobalConfig.isSystemConversationId(conversation.conversationID)
|
_isSystemConversation ? SCChatRouter.systemChat : SCChatRouter.chat;
|
||||||
? SCChatRouter.systemChat
|
|
||||||
: SCChatRouter.chat;
|
|
||||||
SCNavigatorUtils.push(
|
SCNavigatorUtils.push(
|
||||||
context,
|
context,
|
||||||
"$route?conversation=${Uri.encodeComponent(json)}",
|
"$route?conversation=${Uri.encodeComponent(json)}",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showDeleteConfirm();
|
showDeleteConfirm();
|
||||||
@ -387,7 +422,7 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
head(url: user?.userAvatar ?? "", width: 55.w),
|
head(url: _displayAvatar, width: 55.w),
|
||||||
SizedBox(width: 6.w),
|
SizedBox(width: 6.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -401,23 +436,22 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
socialchatNickNameText(
|
socialchatNickNameText(
|
||||||
maxWidth: 152.w,
|
maxWidth: 152.w,
|
||||||
user?.userNickname ?? "",
|
_displayName,
|
||||||
fontSize: 14.sp,
|
fontSize: 14.sp,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
textColor:
|
textColor:
|
||||||
widget.isRoom
|
widget.isRoom
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white,
|
: Colors.white,
|
||||||
type: user?.getVIP()?.name ?? "",
|
type:
|
||||||
|
_isC2CConversation
|
||||||
|
? user?.getVIP()?.name ?? ""
|
||||||
|
: "",
|
||||||
needScroll:
|
needScroll:
|
||||||
(user
|
_displayName.characters.length > 12,
|
||||||
?.userNickname
|
|
||||||
?.characters
|
|
||||||
.length ??
|
|
||||||
0) >
|
|
||||||
12,
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 5.w),
|
SizedBox(width: 5.w),
|
||||||
|
if (_isC2CConversation && user != null)
|
||||||
xb(
|
xb(
|
||||||
user?.userSex ?? 0,
|
user?.userSex ?? 0,
|
||||||
color:
|
color:
|
||||||
@ -521,7 +555,7 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
Provider.of<RtmProvider>(
|
Provider.of<RtmProvider>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).clearC2CHistoryMessage(conversation.conversationID ?? "", true);
|
).clearC2CHistoryMessage(conversation.conversationID, true);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -529,15 +563,10 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadUserInfo() {
|
void loadUserInfo() {
|
||||||
if (!SCGlobalConfig.isSystemConversationId(conversation.conversationID) &&
|
if (!_shouldLoadUserInfo) return;
|
||||||
conversation.conversationID != "customer" &&
|
SCAccountRepository().loadUserInfo(conversation.userID!).then((value) {
|
||||||
conversation.conversationID != "article") {
|
|
||||||
SCAccountRepository().loadUserInfo("${conversation.userID}").then((
|
|
||||||
value,
|
|
||||||
) {
|
|
||||||
user = value;
|
user = value;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1118,12 +1118,16 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildUserInfoLine(BuildContext context) {
|
_buildUserInfoLine(BuildContext context) {
|
||||||
|
final userId = widget.msg.user?.id;
|
||||||
if ((widget.msg.needUpDataUserInfo) ?? false) {
|
if ((widget.msg.needUpDataUserInfo) ?? false) {
|
||||||
SCRoomUtils.roomUsersMap.remove(widget.msg.user?.id ?? "");
|
SCRoomUtils.roomUsersMap.remove(userId ?? "");
|
||||||
}
|
}
|
||||||
return SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""] == null
|
if (userId == null || userId.isEmpty) {
|
||||||
|
return _buildPendingUserInfoLine(context);
|
||||||
|
}
|
||||||
|
return SCRoomUtils.roomUsersMap[userId] == null
|
||||||
? FutureBuilder(
|
? FutureBuilder(
|
||||||
future: SCAccountRepository().loadUserInfo(widget.msg.user?.id ?? ""),
|
future: SCAccountRepository().loadUserInfo(userId),
|
||||||
builder: (ct, AsyncSnapshot<SocialChatUserProfile> snapshot) {
|
builder: (ct, AsyncSnapshot<SocialChatUserProfile> snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
@ -1132,6 +1136,13 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
return _buildNewUserInfoLine(context, user);
|
return _buildNewUserInfoLine(context, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return _buildPendingUserInfoLine(context);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPendingUserInfoLine(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
@ -1182,12 +1193,7 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
fontSize: 13.sp,
|
fontSize: 13.sp,
|
||||||
type: widget.msg.user?.getVIP()?.name ?? "",
|
type: widget.msg.user?.getVIP()?.name ?? "",
|
||||||
needScroll:
|
needScroll:
|
||||||
(widget
|
(widget.msg.user?.userNickname?.characters.length ??
|
||||||
.msg
|
|
||||||
.user
|
|
||||||
?.userNickname
|
|
||||||
?.characters
|
|
||||||
.length ??
|
|
||||||
0) >
|
0) >
|
||||||
10,
|
10,
|
||||||
),
|
),
|
||||||
@ -1216,12 +1222,6 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
|
||||||
)
|
|
||||||
: _buildNewUserInfoLine(
|
|
||||||
context,
|
|
||||||
SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""]!,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) {
|
_buildNewUserInfoLine(BuildContext context, SocialChatUserProfile user) {
|
||||||
|
|||||||
@ -6,15 +6,34 @@ import 'package:yumi/services/audio/rtc_manager.dart';
|
|||||||
import '../../../../modules/room/seat/sc_seat_item.dart';
|
import '../../../../modules/room/seat/sc_seat_item.dart';
|
||||||
|
|
||||||
class RoomSeatWidget extends StatefulWidget {
|
class RoomSeatWidget extends StatefulWidget {
|
||||||
|
const RoomSeatWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RoomSeatWidgetState createState() => _RoomSeatWidgetState();
|
State<RoomSeatWidget> createState() => _RoomSeatWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
||||||
int _lastSeatCount = 0;
|
int _lastSeatCount = 0;
|
||||||
|
|
||||||
|
int _normalizeSeatCount(int? seatCount) {
|
||||||
|
switch (seatCount) {
|
||||||
|
case 5:
|
||||||
|
case 10:
|
||||||
|
case 15:
|
||||||
|
case 20:
|
||||||
|
return seatCount ?? 0;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int _resolvedSeatCount(_RoomSeatLayoutSnapshot snapshot) {
|
int _resolvedSeatCount(_RoomSeatLayoutSnapshot snapshot) {
|
||||||
final int seatCount = snapshot.seatCount;
|
final int liveSeatCount = _normalizeSeatCount(snapshot.seatCount);
|
||||||
|
final int configuredSeatCount = _normalizeSeatCount(
|
||||||
|
snapshot.configuredSeatCount,
|
||||||
|
);
|
||||||
|
final int seatCount =
|
||||||
|
liveSeatCount > 0 ? liveSeatCount : configuredSeatCount;
|
||||||
if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) {
|
if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) {
|
||||||
_lastSeatCount = seatCount;
|
_lastSeatCount = seatCount;
|
||||||
}
|
}
|
||||||
@ -30,6 +49,10 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
|||||||
selector:
|
selector:
|
||||||
(context, provider) => _RoomSeatLayoutSnapshot(
|
(context, provider) => _RoomSeatLayoutSnapshot(
|
||||||
seatCount: provider.roomWheatMap.length,
|
seatCount: provider.roomWheatMap.length,
|
||||||
|
configuredSeatCount:
|
||||||
|
provider.currenRoom?.roomProfile?.roomSetting?.mikeSize
|
||||||
|
?.toInt() ??
|
||||||
|
0,
|
||||||
isExitingCurrentVoiceRoomSession:
|
isExitingCurrentVoiceRoomSession:
|
||||||
provider.isExitingCurrentVoiceRoomSession,
|
provider.isExitingCurrentVoiceRoomSession,
|
||||||
),
|
),
|
||||||
@ -187,10 +210,12 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
|||||||
class _RoomSeatLayoutSnapshot {
|
class _RoomSeatLayoutSnapshot {
|
||||||
const _RoomSeatLayoutSnapshot({
|
const _RoomSeatLayoutSnapshot({
|
||||||
required this.seatCount,
|
required this.seatCount,
|
||||||
|
required this.configuredSeatCount,
|
||||||
required this.isExitingCurrentVoiceRoomSession,
|
required this.isExitingCurrentVoiceRoomSession,
|
||||||
});
|
});
|
||||||
|
|
||||||
final int seatCount;
|
final int seatCount;
|
||||||
|
final int configuredSeatCount;
|
||||||
final bool isExitingCurrentVoiceRoomSession;
|
final bool isExitingCurrentVoiceRoomSession;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -200,10 +225,15 @@ class _RoomSeatLayoutSnapshot {
|
|||||||
}
|
}
|
||||||
return other is _RoomSeatLayoutSnapshot &&
|
return other is _RoomSeatLayoutSnapshot &&
|
||||||
other.seatCount == seatCount &&
|
other.seatCount == seatCount &&
|
||||||
|
other.configuredSeatCount == configuredSeatCount &&
|
||||||
other.isExitingCurrentVoiceRoomSession ==
|
other.isExitingCurrentVoiceRoomSession ==
|
||||||
isExitingCurrentVoiceRoomSession;
|
isExitingCurrentVoiceRoomSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(seatCount, isExitingCurrentVoiceRoomSession);
|
int get hashCode => Object.hash(
|
||||||
|
seatCount,
|
||||||
|
configuredSeatCount,
|
||||||
|
isExitingCurrentVoiceRoomSession,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
42
test/room_seat_widget_test.dart
Normal file
42
test/room_seat_widget_test.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:yumi/modules/room/seat/sc_seat_item.dart';
|
||||||
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/room/seat/room_seat_widget.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets(
|
||||||
|
'RoomSeatWidget falls back to configured mikeSize when mic list is empty',
|
||||||
|
(tester) async {
|
||||||
|
await tester.binding.setSurfaceSize(const Size(1600, 1200));
|
||||||
|
addTearDown(() async {
|
||||||
|
await tester.binding.setSurfaceSize(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
final rtcProvider =
|
||||||
|
RealTimeCommunicationManager()
|
||||||
|
..currenRoom = JoinRoomRes(
|
||||||
|
roomProfile: RoomProfile(roomSetting: RoomSetting(mikeSize: 10)),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ScreenUtilInit(
|
||||||
|
designSize: const Size(375, 812),
|
||||||
|
builder:
|
||||||
|
(_, __) =>
|
||||||
|
ChangeNotifierProvider<RealTimeCommunicationManager>.value(
|
||||||
|
value: rtcProvider,
|
||||||
|
child: MaterialApp(home: Scaffold(body: RoomSeatWidget())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(RoomSeatWidget), findsOneWidget);
|
||||||
|
expect(find.byType(SCSeatItem), findsNWidgets(10));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user