退出房间删除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,36 +248,35 @@ 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,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
if (_canOpenFriendProfile)
|
||||||
behavior: HitTestBehavior.opaque,
|
GestureDetector(
|
||||||
onTap: () {
|
behavior: HitTestBehavior.opaque,
|
||||||
SCNavigatorUtils.push(
|
onTap: () {
|
||||||
context,
|
SCNavigatorUtils.push(
|
||||||
replace: true,
|
context,
|
||||||
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
|
replace: true,
|
||||||
);
|
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
|
||||||
},
|
);
|
||||||
child: Container(
|
},
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
child: Container(
|
||||||
child: Icon(
|
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||||
Icons.more_vert,
|
child: Icon(
|
||||||
size: 22.w,
|
Icons.more_vert,
|
||||||
color: Colors.white,
|
size: 22.w,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(child: _msgList()),
|
Expanded(child: _msgList()),
|
||||||
@ -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
|
count: 30,
|
||||||
// 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的id
|
lastMsgID: currentConversationMessageList.last.msgID,
|
||||||
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
|
);
|
||||||
await TencentImSDKPlugin.v2TIMManager
|
|
||||||
.getMessageManager()
|
|
||||||
.getC2CHistoryMessageList(
|
|
||||||
userID: currentConversation!.userID ?? "",
|
|
||||||
count: 30,
|
|
||||||
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 {
|
||||||
await TencentImSDKPlugin.v2TIMManager.quitGroup(groupID: groupID);
|
if (groupID.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
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) {
|
||||||
.getConversationManager()
|
return;
|
||||||
.deleteConversation(conversationID: conversationID); // 需要清空记录的用户id
|
|
||||||
if (clearC2CHistoryMessageRes.code == 0) {
|
|
||||||
// 清除成功
|
|
||||||
if (needShowToast) {
|
|
||||||
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
|
|
||||||
}
|
|
||||||
initConversation();
|
|
||||||
} else {
|
|
||||||
// 清除失败,可以查看 clearC2CHistoryMessageRes.desc 获取错误描述
|
|
||||||
}
|
}
|
||||||
|
V2TimCallback deleteConversationRes = await TencentImSDKPlugin.v2TIMManager
|
||||||
|
.getConversationManager()
|
||||||
|
.deleteConversation(conversationID: conversationID);
|
||||||
|
if (deleteConversationRes.code == 0 && needShowToast && context != null) {
|
||||||
|
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
|
||||||
|
}
|
||||||
|
await initConversation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearC2CHistoryMessage(String conversationID, bool needShowToast) async {
|
||||||
|
await deleteConversationById(conversationID, needShowToast: needShowToast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,16 @@ import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
|
|
||||||
import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart';
|
import '../../shared/data_sources/models/enum/sc_heartbeat_status.dart';
|
||||||
|
|
||||||
class SCHeartbeatUtils {
|
class SCHeartbeatUtils {
|
||||||
static Timer? _h;
|
static Timer? _h;
|
||||||
static Timer? _a;
|
static Timer? _a;
|
||||||
static String _c = SCHeartbeatStatus.ONLINE.name;
|
static String _c = SCHeartbeatStatus.ONLINE.name;
|
||||||
static String? _r;
|
static String? _r;
|
||||||
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,
|
||||||
@ -42,14 +42,14 @@ class SCHeartbeatUtils {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cancelTimer() {
|
static void cancelTimer() {
|
||||||
_h?.cancel();
|
_h?.cancel();
|
||||||
_h = null;
|
_h = null;
|
||||||
_r = null;
|
_r = null;
|
||||||
cancelAnchorTimer();
|
cancelAnchorTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
///上麦主播发送心跳
|
///上麦主播发送心跳
|
||||||
static void scheduleAnchorHeartbeat(String roomId) async {
|
static void scheduleAnchorHeartbeat(String roomId) async {
|
||||||
cancelAnchorTimer();
|
cancelAnchorTimer();
|
||||||
@ -60,10 +60,10 @@ class SCHeartbeatUtils {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cancelAnchorTimer() {
|
static void cancelAnchorTimer() {
|
||||||
_a?.cancel();
|
_a?.cancel();
|
||||||
_a = null;
|
_a = null;
|
||||||
_u = false;
|
_u = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -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,96 +1136,92 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
return _buildNewUserInfoLine(context, user);
|
return _buildNewUserInfoLine(context, user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Row(
|
return _buildPendingUserInfoLine(context);
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
widget.onClick(widget.msg.user);
|
|
||||||
},
|
|
||||||
child: Stack(
|
|
||||||
alignment: AlignmentDirectional.bottomEnd,
|
|
||||||
children: [
|
|
||||||
head(
|
|
||||||
url: widget.msg.user?.userAvatar ?? "",
|
|
||||||
width: 48.w,
|
|
||||||
// headdress: msg.user?.getHeaddress()?.sourceUrl,
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
child: msgRoleTag(widget.msg.role ?? "", width: 14.w),
|
|
||||||
bottom: 2.w,
|
|
||||||
right: 2.w,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(width: 3.w),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
getWealthLevel(
|
|
||||||
widget.msg.user?.wealthLevel ?? 0,
|
|
||||||
width: 48.w,
|
|
||||||
height: 23.w,
|
|
||||||
fontSize: 10.sp,
|
|
||||||
),
|
|
||||||
getUserLevel(
|
|
||||||
widget.msg.user?.charmLevel ?? 0,
|
|
||||||
width: 48.w,
|
|
||||||
height: 23.w,
|
|
||||||
fontSize: 10.sp,
|
|
||||||
),
|
|
||||||
SizedBox(width: 3.w),
|
|
||||||
socialchatNickNameText(
|
|
||||||
maxWidth: 120.w,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
widget.msg.user?.userNickname ?? "",
|
|
||||||
fontSize: 13.sp,
|
|
||||||
type: widget.msg.user?.getVIP()?.name ?? "",
|
|
||||||
needScroll:
|
|
||||||
(widget
|
|
||||||
.msg
|
|
||||||
.user
|
|
||||||
?.userNickname
|
|
||||||
?.characters
|
|
||||||
.length ??
|
|
||||||
0) >
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
widget.msg.user?.getVIP() != null
|
|
||||||
? netImage(
|
|
||||||
url: widget.msg.user?.getVIP()?.cover ?? "",
|
|
||||||
width: 25.w,
|
|
||||||
height: 25.w,
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
_buildMedals(
|
|
||||||
(widget.msg.user?.wearBadge?.where((item) {
|
|
||||||
return item.use ?? false;
|
|
||||||
}).toList() ??
|
|
||||||
[]),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: _buildNewUserInfoLine(
|
: _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
|
||||||
context,
|
}
|
||||||
SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""]!,
|
|
||||||
);
|
Widget _buildPendingUserInfoLine(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
widget.onClick(widget.msg.user);
|
||||||
|
},
|
||||||
|
child: Stack(
|
||||||
|
alignment: AlignmentDirectional.bottomEnd,
|
||||||
|
children: [
|
||||||
|
head(
|
||||||
|
url: widget.msg.user?.userAvatar ?? "",
|
||||||
|
width: 48.w,
|
||||||
|
// headdress: msg.user?.getHeaddress()?.sourceUrl,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
child: msgRoleTag(widget.msg.role ?? "", width: 14.w),
|
||||||
|
bottom: 2.w,
|
||||||
|
right: 2.w,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 3.w),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
getWealthLevel(
|
||||||
|
widget.msg.user?.wealthLevel ?? 0,
|
||||||
|
width: 48.w,
|
||||||
|
height: 23.w,
|
||||||
|
fontSize: 10.sp,
|
||||||
|
),
|
||||||
|
getUserLevel(
|
||||||
|
widget.msg.user?.charmLevel ?? 0,
|
||||||
|
width: 48.w,
|
||||||
|
height: 23.w,
|
||||||
|
fontSize: 10.sp,
|
||||||
|
),
|
||||||
|
SizedBox(width: 3.w),
|
||||||
|
socialchatNickNameText(
|
||||||
|
maxWidth: 120.w,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
widget.msg.user?.userNickname ?? "",
|
||||||
|
fontSize: 13.sp,
|
||||||
|
type: widget.msg.user?.getVIP()?.name ?? "",
|
||||||
|
needScroll:
|
||||||
|
(widget.msg.user?.userNickname?.characters.length ??
|
||||||
|
0) >
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
widget.msg.user?.getVIP() != null
|
||||||
|
? netImage(
|
||||||
|
url: widget.msg.user?.getVIP()?.cover ?? "",
|
||||||
|
width: 25.w,
|
||||||
|
height: 25.w,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
_buildMedals(
|
||||||
|
(widget.msg.user?.wearBadge?.where((item) {
|
||||||
|
return item.use ?? false;
|
||||||
|
}).toList() ??
|
||||||
|
[]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_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