退出房间删除rtc, 消息列表判断
This commit is contained in:
parent
15ac93c737
commit
a5bc029113
@ -78,10 +78,7 @@ class SCGlobalConfig {
|
||||
|
||||
static Set<String> get systemConversationIds {
|
||||
return {
|
||||
"administrator",
|
||||
...systemUserIds
|
||||
.where((element) => element != "administrator")
|
||||
.map((element) => "c2c_$element"),
|
||||
for (final userId in systemUserIds) ...{userId, "c2c_$userId"},
|
||||
};
|
||||
}
|
||||
|
||||
@ -93,7 +90,7 @@ class SCGlobalConfig {
|
||||
if (conversationId == null || conversationId.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return systemConversationIds.contains(conversationId);
|
||||
return systemUserIds.contains(_normalizeConversationUserId(conversationId));
|
||||
}
|
||||
|
||||
static bool isSystemUserId(String? userId) {
|
||||
|
||||
@ -24,6 +24,7 @@ import 'package:marquee/marquee.dart';
|
||||
import 'package:provider/provider.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/conversation_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/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_path_utils.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/modules/index/main_route.dart';
|
||||
|
||||
@ -104,6 +104,34 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
List<String> coinsTitles = ["100", "1000", "10000", "50000"];
|
||||
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
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -119,7 +147,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
|
||||
loadMsg();
|
||||
|
||||
if (friend == null) {
|
||||
if (_isC2CConversation && friend == null) {
|
||||
loadFriend();
|
||||
}
|
||||
|
||||
@ -220,36 +248,35 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
child: socialchatNickNameText(
|
||||
friend?.userNickname ?? "",
|
||||
_conversationTitle,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
textColor: Colors.white,
|
||||
type: "",
|
||||
needScroll:
|
||||
(friend?.userNickname?.characters.length ??
|
||||
0) >
|
||||
22,
|
||||
_conversationTitle.characters.length > 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
SCNavigatorUtils.push(
|
||||
context,
|
||||
replace: true,
|
||||
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
child: Icon(
|
||||
Icons.more_vert,
|
||||
size: 22.w,
|
||||
color: Colors.white,
|
||||
if (_canOpenFriendProfile)
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
SCNavigatorUtils.push(
|
||||
context,
|
||||
replace: true,
|
||||
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == friend?.id}&tageId=${friend?.id}",
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
child: Icon(
|
||||
Icons.more_vert,
|
||||
size: 22.w,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(child: _msgList()),
|
||||
@ -262,10 +289,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
top: false,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
if (!SCGlobalConfig.isSystemConversationId(
|
||||
currentConversation?.conversationID,
|
||||
))
|
||||
_input(),
|
||||
if (_canSendConversationMessage) _input(),
|
||||
_tools(provider),
|
||||
_emoji(),
|
||||
// _fahongbao(),
|
||||
@ -285,18 +309,18 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
}
|
||||
|
||||
void loadFriend() {
|
||||
if (SCGlobalConfig.isSystemConversationId(
|
||||
currentConversation?.conversationID,
|
||||
) ||
|
||||
SCGlobalConfig.isSystemUserId(currentConversation?.userID)) {
|
||||
final userId = currentConversation?.userID;
|
||||
if (!_isC2CConversation ||
|
||||
_isSystemConversation ||
|
||||
SCGlobalConfig.isSystemUserId(userId) ||
|
||||
userId == null ||
|
||||
userId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
SCLoadingManager.show();
|
||||
Future.wait([
|
||||
SCAccountRepository().loadUserInfo("${currentConversation?.userID}"),
|
||||
SCAccountRepository().friendRelationCheck(
|
||||
"${currentConversation?.userID}",
|
||||
),
|
||||
SCAccountRepository().loadUserInfo(userId),
|
||||
SCAccountRepository().friendRelationCheck(userId),
|
||||
])
|
||||
.then((result) {
|
||||
SCLoadingManager.hide();
|
||||
@ -318,19 +342,10 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
onLoading: () async {
|
||||
print('onLoading');
|
||||
if (currentConversationMessageList.isNotEmpty) {
|
||||
// 拉取单聊历史消息
|
||||
// 首次拉取,lastMsgID 设置为 null
|
||||
// 再次拉取时,lastMsgID 可以使用返回的消息列表中的最后一条消息的id
|
||||
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>;
|
||||
final messages = await _loadHistoryMessages(
|
||||
count: 30,
|
||||
lastMsgID: currentConversationMessageList.last.msgID,
|
||||
);
|
||||
print('加载前:${currentConversationMessageList.length}');
|
||||
currentConversationMessageList.addAll(messages);
|
||||
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 {
|
||||
// 1. 在初始化时,添加高级消息监听器
|
||||
TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(
|
||||
@ -404,19 +467,9 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
},
|
||||
),
|
||||
);
|
||||
V2TimValueCallback<List<V2TimMessage>> v2timValueCallback =
|
||||
await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.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();
|
||||
final messages = await _loadHistoryMessages(count: 100);
|
||||
print("messages : ${messages.length}");
|
||||
currentConversationMessageList.clear();
|
||||
|
||||
for (var msg in messages) {
|
||||
if (!msg.isSelf! && msg.isPeerRead!) {
|
||||
@ -425,7 +478,8 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
.sendMessageReadReceipts(messageIDList: [msg.msgID!]);
|
||||
}
|
||||
}
|
||||
currentConversationMessageList.insertAll(0, messages ?? []);
|
||||
currentConversationMessageList.insertAll(0, messages);
|
||||
await _markCurrentConversationAsRead();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@ -767,7 +821,11 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
if (message.userID != currentConversation?.userID) return;
|
||||
if (_isGroupConversation) {
|
||||
if (message.groupID != currentConversation?.groupID) return;
|
||||
} else if (message.userID != currentConversation?.userID) {
|
||||
return;
|
||||
}
|
||||
|
||||
int? index;
|
||||
for (var element in currentConversationMessageList) {
|
||||
@ -788,14 +846,12 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
||||
return;
|
||||
}
|
||||
if (index != null) {
|
||||
currentConversationMessageList.removeAt(index!);
|
||||
currentConversationMessageList.insert(index!, message);
|
||||
currentConversationMessageList.removeAt(index);
|
||||
currentConversationMessageList.insert(index, message);
|
||||
} else {
|
||||
currentConversationMessageList.insert(0, message);
|
||||
}
|
||||
await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.markC2CMessageAsRead(userID: currentConversation!.userID!);
|
||||
await _markCurrentConversationAsRead();
|
||||
//发送已读回执
|
||||
await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
@ -849,7 +905,7 @@ class _MessageItem extends StatelessWidget {
|
||||
}
|
||||
String time = SCMDateUtils.formatMessageTime(
|
||||
context,
|
||||
DateTime.fromMillisecondsSinceEpoch(timestamp ?? 0),
|
||||
DateTime.fromMillisecondsSinceEpoch(timestamp),
|
||||
);
|
||||
if (message.status == MessageStatus.V2TIM_MSG_STATUS_LOCAL_REVOKED) {
|
||||
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({
|
||||
required ClientRoleType clientRole,
|
||||
required bool muted,
|
||||
@ -1058,6 +1078,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
),
|
||||
);
|
||||
|
||||
await _bootstrapRoomHeartbeatState();
|
||||
|
||||
///获取麦位
|
||||
retrieveMicrophoneList();
|
||||
fetchOnlineUsersList();
|
||||
|
||||
@ -133,22 +133,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
///客服
|
||||
SocialChatUserProfile? customerInfo;
|
||||
|
||||
bool _isSystemConversation(V2TimConversation? conversation) {
|
||||
if (conversation == null) {
|
||||
return false;
|
||||
}
|
||||
return SCGlobalConfig.isSystemConversationId(conversation.conversationID) ||
|
||||
SCGlobalConfig.isSystemUserId(conversation.userID);
|
||||
}
|
||||
|
||||
int _systemUnreadCount() {
|
||||
int count = 0;
|
||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
||||
count += conversationMap[conversationId]?.unreadCount ?? 0;
|
||||
for (final conversation in conversationMap.values) {
|
||||
if (_isSystemConversation(conversation)) {
|
||||
count += conversation.unreadCount ?? 0;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
V2TimConversation getPreferredSystemConversation() {
|
||||
final systemConversations =
|
||||
conversationMap.values
|
||||
.where(
|
||||
(element) =>
|
||||
SCGlobalConfig.isSystemConversationId(element.conversationID),
|
||||
)
|
||||
.toList()
|
||||
conversationMap.values.where(_isSystemConversation).toList()
|
||||
..sort((e1, e2) {
|
||||
final time1 = e1.lastMessage?.timestamp ?? 0;
|
||||
final time2 = e2.lastMessage?.timestamp ?? 0;
|
||||
@ -169,6 +174,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
void getConversationList() {
|
||||
List<V2TimConversation> list = conversationMap.values.toList();
|
||||
list.removeWhere((element) {
|
||||
if (_isSystemConversation(element)) {
|
||||
return true;
|
||||
}
|
||||
if (element.conversationID == "c2c_${customerInfo?.id}") {
|
||||
return true;
|
||||
}
|
||||
@ -501,7 +509,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
|
||||
_onNew1v1Message(V2TimMessage? message, {String? msgId}) async {
|
||||
for (var element in conversationList) {
|
||||
if (message?.userID == element?.userID) {
|
||||
if (message?.userID == element.userID) {
|
||||
element.lastMessage = message;
|
||||
if (onNewMessageCurrentConversationListener == null) {
|
||||
element.unreadCount = element.unreadCount! + 1;
|
||||
@ -1740,7 +1748,14 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
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) {
|
||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
||||
conversationMap[conversationId]?.unreadCount = 0;
|
||||
for (final conversation in conversationMap.values) {
|
||||
if (_isSystemConversation(conversation)) {
|
||||
conversation.unreadCount = 0;
|
||||
}
|
||||
}
|
||||
systemUnReadCount = 0;
|
||||
notifyListeners();
|
||||
@ -1870,21 +1887,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearC2CHistoryMessage(String conversationID, bool needShowToast) async {
|
||||
// 清空单聊本地及云端的消息(不删除会话)
|
||||
|
||||
V2TimCallback clearC2CHistoryMessageRes = await TencentImSDKPlugin
|
||||
.v2TIMManager
|
||||
.getConversationManager()
|
||||
.deleteConversation(conversationID: conversationID); // 需要清空记录的用户id
|
||||
if (clearC2CHistoryMessageRes.code == 0) {
|
||||
// 清除成功
|
||||
if (needShowToast) {
|
||||
SCTts.show(SCAppLocalizations.of(context!)!.operationSuccessful);
|
||||
}
|
||||
initConversation();
|
||||
} else {
|
||||
// 清除失败,可以查看 clearC2CHistoryMessageRes.desc 获取错误描述
|
||||
Future<void> deleteConversationById(
|
||||
String conversationID, {
|
||||
bool needShowToast = false,
|
||||
}) async {
|
||||
if (conversationID.isEmpty) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ class SCHeartbeatUtils {
|
||||
static bool _u = false;
|
||||
|
||||
///定时发送心跳
|
||||
static void scheduleHeartbeat(
|
||||
static Future<void> scheduleHeartbeat(
|
||||
String status,
|
||||
bool upMick, {
|
||||
String? roomId,
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
@ -296,6 +295,39 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
V2TimConversation get conversation => widget.conversation;
|
||||
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
|
||||
void initState() {
|
||||
loadUserInfo();
|
||||
@ -351,23 +383,26 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
// print('time :${Platform.isAndroid ? (conversation.lastMsg?.timestamp ?? 0) * 1000 : conversation.lastMsg?.timestamp}');
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
if (conversation != null) {
|
||||
conversation.unreadCount = 0;
|
||||
var bool = await Provider.of<RtmProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
).startConversation(conversation);
|
||||
if (!bool) return;
|
||||
var json = jsonEncode(widget.conversation.toJson());
|
||||
final route =
|
||||
SCGlobalConfig.isSystemConversationId(conversation.conversationID)
|
||||
? SCChatRouter.systemChat
|
||||
: SCChatRouter.chat;
|
||||
SCNavigatorUtils.push(
|
||||
context,
|
||||
"$route?conversation=${Uri.encodeComponent(json)}",
|
||||
);
|
||||
if (_isC2CConversation && (conversation.userID?.isEmpty ?? true)) {
|
||||
return;
|
||||
}
|
||||
if (conversation.type == ConversationType.V2TIM_GROUP &&
|
||||
(conversation.groupID?.isEmpty ?? true)) {
|
||||
return;
|
||||
}
|
||||
conversation.unreadCount = 0;
|
||||
var bool = await Provider.of<RtmProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
).startConversation(conversation);
|
||||
if (!bool) return;
|
||||
var json = jsonEncode(widget.conversation.toJson());
|
||||
final route =
|
||||
_isSystemConversation ? SCChatRouter.systemChat : SCChatRouter.chat;
|
||||
SCNavigatorUtils.push(
|
||||
context,
|
||||
"$route?conversation=${Uri.encodeComponent(json)}",
|
||||
);
|
||||
},
|
||||
onLongPress: () {
|
||||
showDeleteConfirm();
|
||||
@ -387,7 +422,7 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
head(url: user?.userAvatar ?? "", width: 55.w),
|
||||
head(url: _displayAvatar, width: 55.w),
|
||||
SizedBox(width: 6.w),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@ -401,30 +436,29 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
children: <Widget>[
|
||||
socialchatNickNameText(
|
||||
maxWidth: 152.w,
|
||||
user?.userNickname ?? "",
|
||||
_displayName,
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
textColor:
|
||||
widget.isRoom
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
type: user?.getVIP()?.name ?? "",
|
||||
type:
|
||||
_isC2CConversation
|
||||
? user?.getVIP()?.name ?? ""
|
||||
: "",
|
||||
needScroll:
|
||||
(user
|
||||
?.userNickname
|
||||
?.characters
|
||||
.length ??
|
||||
0) >
|
||||
12,
|
||||
_displayName.characters.length > 12,
|
||||
),
|
||||
SizedBox(width: 5.w),
|
||||
xb(
|
||||
user?.userSex ?? 0,
|
||||
color:
|
||||
widget.isRoom
|
||||
? Colors.white54
|
||||
: Colors.black26,
|
||||
),
|
||||
if (_isC2CConversation && user != null)
|
||||
xb(
|
||||
user?.userSex ?? 0,
|
||||
color:
|
||||
widget.isRoom
|
||||
? Colors.white54
|
||||
: Colors.black26,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -521,7 +555,7 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
Provider.of<RtmProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
).clearC2CHistoryMessage(conversation.conversationID ?? "", true);
|
||||
).clearC2CHistoryMessage(conversation.conversationID, true);
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -529,15 +563,10 @@ class _ConversationItemState extends State<ConversationItem> {
|
||||
}
|
||||
|
||||
void loadUserInfo() {
|
||||
if (!SCGlobalConfig.isSystemConversationId(conversation.conversationID) &&
|
||||
conversation.conversationID != "customer" &&
|
||||
conversation.conversationID != "article") {
|
||||
SCAccountRepository().loadUserInfo("${conversation.userID}").then((
|
||||
value,
|
||||
) {
|
||||
user = value;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
if (!_shouldLoadUserInfo) return;
|
||||
SCAccountRepository().loadUserInfo(conversation.userID!).then((value) {
|
||||
user = value;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1118,12 +1118,16 @@ class _MsgItemState extends State<MsgItem> {
|
||||
}
|
||||
|
||||
_buildUserInfoLine(BuildContext context) {
|
||||
final userId = widget.msg.user?.id;
|
||||
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(
|
||||
future: SCAccountRepository().loadUserInfo(widget.msg.user?.id ?? ""),
|
||||
future: SCAccountRepository().loadUserInfo(userId),
|
||||
builder: (ct, AsyncSnapshot<SocialChatUserProfile> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.hasData) {
|
||||
@ -1132,96 +1136,92 @@ class _MsgItemState extends State<MsgItem> {
|
||||
return _buildNewUserInfoLine(context, user);
|
||||
}
|
||||
}
|
||||
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() ??
|
||||
[]),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
return _buildPendingUserInfoLine(context);
|
||||
},
|
||||
)
|
||||
: _buildNewUserInfoLine(
|
||||
context,
|
||||
SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""]!,
|
||||
);
|
||||
: _buildNewUserInfoLine(context, SCRoomUtils.roomUsersMap[userId]!);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -6,15 +6,34 @@ import 'package:yumi/services/audio/rtc_manager.dart';
|
||||
import '../../../../modules/room/seat/sc_seat_item.dart';
|
||||
|
||||
class RoomSeatWidget extends StatefulWidget {
|
||||
const RoomSeatWidget({super.key});
|
||||
|
||||
@override
|
||||
_RoomSeatWidgetState createState() => _RoomSeatWidgetState();
|
||||
State<RoomSeatWidget> createState() => _RoomSeatWidgetState();
|
||||
}
|
||||
|
||||
class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
||||
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) {
|
||||
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) {
|
||||
_lastSeatCount = seatCount;
|
||||
}
|
||||
@ -30,6 +49,10 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
||||
selector:
|
||||
(context, provider) => _RoomSeatLayoutSnapshot(
|
||||
seatCount: provider.roomWheatMap.length,
|
||||
configuredSeatCount:
|
||||
provider.currenRoom?.roomProfile?.roomSetting?.mikeSize
|
||||
?.toInt() ??
|
||||
0,
|
||||
isExitingCurrentVoiceRoomSession:
|
||||
provider.isExitingCurrentVoiceRoomSession,
|
||||
),
|
||||
@ -187,10 +210,12 @@ class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
||||
class _RoomSeatLayoutSnapshot {
|
||||
const _RoomSeatLayoutSnapshot({
|
||||
required this.seatCount,
|
||||
required this.configuredSeatCount,
|
||||
required this.isExitingCurrentVoiceRoomSession,
|
||||
});
|
||||
|
||||
final int seatCount;
|
||||
final int configuredSeatCount;
|
||||
final bool isExitingCurrentVoiceRoomSession;
|
||||
|
||||
@override
|
||||
@ -200,10 +225,15 @@ class _RoomSeatLayoutSnapshot {
|
||||
}
|
||||
return other is _RoomSeatLayoutSnapshot &&
|
||||
other.seatCount == seatCount &&
|
||||
other.configuredSeatCount == configuredSeatCount &&
|
||||
other.isExitingCurrentVoiceRoomSession ==
|
||||
isExitingCurrentVoiceRoomSession;
|
||||
}
|
||||
|
||||
@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