修改消息和rtc

This commit is contained in:
zhx 2026-05-23 19:08:07 +08:00
parent 1e7261927e
commit 8f95b72fd0
4 changed files with 510 additions and 21 deletions

View File

@ -1224,6 +1224,11 @@ class _MessageItem extends StatelessWidget {
), ),
); );
} }
final customData = _customDataFromMessage(message);
if (customData != null &&
_isDuplicateCpResultMessageForConversation(customData)) {
return const SizedBox.shrink();
}
final cpRelationFormedCard = _cpRelationFormedMessageCard(); final cpRelationFormedCard = _cpRelationFormedMessageCard();
final cpStatusLineText = _cpStatusLineTextForMessage(); final cpStatusLineText = _cpStatusLineTextForMessage();
return Container( return Container(
@ -1381,7 +1386,7 @@ class _MessageItem extends StatelessWidget {
return GestureDetector( return GestureDetector(
onLongPress: () => _showMsgItemMenu(ct, ""), onLongPress: () => _showMsgItemMenu(ct, ""),
onTap: onTap:
() => _showCpInviteDialogFromMessage(data, cpInvite, actionState), () => _openCpInviteDialogFromMessage(data, cpInvite, actionState),
child: Container( child: Container(
width: 251.w, width: 251.w,
height: 64.w, height: 64.w,
@ -1451,6 +1456,30 @@ class _MessageItem extends StatelessWidget {
); );
} }
void _openCpInviteDialogFromMessage(
Map<String, dynamic> data,
SCSystemInvitMessageRes? cpInvite,
RoomCpInviteActionState actionState,
) {
unawaited(_openCpInviteDialogFromMessageAsync(data, cpInvite, actionState));
}
Future<void> _openCpInviteDialogFromMessageAsync(
Map<String, dynamic> data,
SCSystemInvitMessageRes? cpInvite,
RoomCpInviteActionState actionState,
) async {
final latestState = await _latestCpInviteStateForMessage(data);
if (!context.mounted) {
return;
}
final resolvedActionState =
latestState == _CpInviteMessageState.pending
? actionState
: _cpInviteActionState(latestState);
_showCpInviteDialogFromMessage(data, cpInvite, resolvedActionState);
}
Widget _buildCpInviteGiftThumb(String giftCover) { Widget _buildCpInviteGiftThumb(String giftCover) {
final child = final child =
giftCover.isNotEmpty giftCover.isNotEmpty
@ -1623,12 +1652,22 @@ class _MessageItem extends StatelessWidget {
return null; return null;
} }
final localizations = SCAppLocalizations.of(context)!; final localizations = SCAppLocalizations.of(context)!;
final relationType = _cpRelationTypeFromMessageData(data);
if (state == _CpInviteMessageState.expired) { if (state == _CpInviteMessageState.expired) {
if (relationType != "CP") {
return "The ${scCpRelationPossessiveLabel(relationType)} invitation timed out.";
}
return localizations.closeFriendInviteTimedOut; return localizations.closeFriendInviteTimedOut;
} }
if (relationType == "CP") {
return _isCurrentUserCpInviter(data)
? localizations.otherDeclinedCloseFriendInvite
: localizations.youDeclinedCloseFriendInvite;
}
final relationLabel = scCpRelationPossessiveLabel(relationType);
return _isCurrentUserCpInviter(data) return _isCurrentUserCpInviter(data)
? localizations.otherDeclinedCloseFriendInvite ? "The other party declined your $relationLabel invitation."
: localizations.youDeclinedCloseFriendInvite; : "You declined the other party's $relationLabel invitation.";
} }
void _showCpInviteDialogFromMessage( void _showCpInviteDialogFromMessage(
@ -1782,14 +1821,15 @@ class _MessageItem extends StatelessWidget {
cpInvite != null && cpInvite != null &&
actionState == RoomCpInviteActionState.pending && actionState == RoomCpInviteActionState.pending &&
applyId.isNotEmpty applyId.isNotEmpty
? () => _processCpInviteFromMessage(applyId, cpInvite, true) ? () => _processCpInviteFromMessage(applyId, cpInvite, true, data)
: null, : null,
onReject: onReject:
!isInviter && !isInviter &&
cpInvite != null && cpInvite != null &&
actionState == RoomCpInviteActionState.pending && actionState == RoomCpInviteActionState.pending &&
applyId.isNotEmpty applyId.isNotEmpty
? () => _processCpInviteFromMessage(applyId, cpInvite, false) ? () =>
_processCpInviteFromMessage(applyId, cpInvite, false, data)
: null, : null,
); );
} }
@ -1798,16 +1838,37 @@ class _MessageItem extends StatelessWidget {
String applyId, String applyId,
SCSystemInvitMessageRes invite, SCSystemInvitMessageRes invite,
bool agree, bool agree,
Map<String, dynamic> data,
) { ) {
unawaited(_processCpInviteFromMessageAsync(applyId, invite, agree)); unawaited(_processCpInviteFromMessageAsync(applyId, invite, agree, data));
} }
Future<void> _processCpInviteFromMessageAsync( Future<void> _processCpInviteFromMessageAsync(
String applyId, String applyId,
SCSystemInvitMessageRes invite, SCSystemInvitMessageRes invite,
bool agree, bool agree,
Map<String, dynamic> data,
) async { ) async {
_cpMessageLog('process applyId=$applyId agree=$agree'); _cpMessageLog('process applyId=$applyId agree=$agree');
final latestState = await _latestCpInviteStateForMessage(data);
if (latestState != _CpInviteMessageState.pending) {
_cpMessageLog(
'skip process applyId=$applyId agree=$agree latestState=$latestState',
);
if (!context.mounted) {
return;
}
_showCpInviteDialogFromMessage(
data,
invite,
_cpInviteActionState(latestState),
);
updateCall();
return;
}
if (!context.mounted) {
return;
}
final profileManager = Provider.of<SocialChatUserProfileManager>( final profileManager = Provider.of<SocialChatUserProfileManager>(
context, context,
listen: false, listen: false,
@ -1821,6 +1882,9 @@ class _MessageItem extends StatelessWidget {
_cpMessageLog('process failed applyId=$applyId agree=$agree'); _cpMessageLog('process failed applyId=$applyId agree=$agree');
return; return;
} }
if (!context.mounted) {
return;
}
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag); SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
_cpMessageLog( _cpMessageLog(
'process submitted applyId=$applyId agree=$agree ' 'process submitted applyId=$applyId agree=$agree '
@ -1830,6 +1894,58 @@ class _MessageItem extends StatelessWidget {
updateCall(); updateCall();
} }
Future<_CpInviteMessageState> _latestCpInviteStateForMessage(
Map<String, dynamic> data,
) async {
final localState = _cpInviteStateForMessage(data);
if (localState != _CpInviteMessageState.pending) {
return localState;
}
final applyId = _cpApplyIdFromData(data);
if (applyId.isEmpty) {
return localState;
}
try {
final status = await SCAccountRepository().cpRelationshipApplyStatus(
applyId,
);
final backendState = _cpInviteStateFromBackendStatus(status);
if (backendState != null) {
return backendState;
}
} catch (error) {
_cpMessageLog('query apply status failed applyId=$applyId error=$error');
}
return _cpInviteSecondsLeft(data) <= 0
? _CpInviteMessageState.expired
: localState;
}
_CpInviteMessageState? _cpInviteStateFromBackendStatus(String? status) {
final text = status?.trim().toUpperCase() ?? "";
if (text.isEmpty || text == "WAIT" || text == "WAITING") {
return text.isEmpty ? null : _CpInviteMessageState.pending;
}
if (text.contains("EXPIRE") ||
text.contains("TIMEOUT") ||
text.contains("OVERDUE")) {
return _CpInviteMessageState.expired;
}
if (text.contains("REFUSE") ||
text.contains("REJECT") ||
text.contains("DECLIN") ||
text.contains("DENY")) {
return _CpInviteMessageState.rejected;
}
if (text.contains("AGREE") ||
text.contains("ACCEPT") ||
text.contains("APPROVE") ||
text.contains("ESTABLISH")) {
return _CpInviteMessageState.accepted;
}
return null;
}
Map<String, dynamic>? _customDataFromMessage(V2TimMessage msg) { Map<String, dynamic>? _customDataFromMessage(V2TimMessage msg) {
if (msg.elemType != MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) { if (msg.elemType != MessageElemType.V2TIM_ELEM_TYPE_CUSTOM) {
return null; return null;
@ -1891,6 +2007,60 @@ class _MessageItem extends StatelessWidget {
return _CpInviteMessageState.pending; return _CpInviteMessageState.pending;
} }
bool _isDuplicateCpResultMessageForConversation(Map<String, dynamic> data) {
final currentKey = _cpResultMessageDedupKey(data);
if (currentKey.isEmpty) {
return false;
}
final currentIndex = currentConversationMessageList.indexWhere(
_isCurrentConversationMessage,
);
if (currentIndex <= 0) {
return false;
}
for (var i = 0; i < currentIndex; i++) {
final otherData = _customDataFromMessage(
currentConversationMessageList[i],
);
if (otherData == null) {
continue;
}
if (_cpResultMessageDedupKey(otherData) == currentKey) {
return true;
}
}
return false;
}
bool _isCurrentConversationMessage(V2TimMessage item) {
final currentMsgId = message.msgID?.trim() ?? "";
final itemMsgId = item.msgID?.trim() ?? "";
if (currentMsgId.isNotEmpty && itemMsgId.isNotEmpty) {
return currentMsgId == itemMsgId;
}
final currentSeq = message.seq?.trim() ?? "";
final itemSeq = item.seq?.trim() ?? "";
if (currentSeq.isNotEmpty && itemSeq.isNotEmpty) {
return currentSeq == itemSeq && item.timestamp == message.timestamp;
}
return identical(item, message);
}
String _cpResultMessageDedupKey(Map<String, dynamic> data) {
if (!_isCpInvitePayload(data)) {
return "";
}
final state = _cpInviteStateFromData(data);
if (state == _CpInviteMessageState.pending) {
return "";
}
final applyId = _cpApplyIdFromData(data);
if (applyId.isEmpty) {
return "";
}
return "$applyId|${state.name}";
}
_CpInviteMessageState _cpInviteStateFromData(Map<String, dynamic> data) { _CpInviteMessageState _cpInviteStateFromData(Map<String, dynamic> data) {
final content = _cpContentMap(data); final content = _cpContentMap(data);
final values = <String>[ final values = <String>[
@ -2145,6 +2315,20 @@ class _MessageItem extends StatelessWidget {
], fallback: ""); ], fallback: "");
} }
String _cpRelationTypeFromMessageData(Map<String, dynamic> data) {
final invite = _cpInviteFromMessageData(data);
final content = _cpContentMap(data);
return scNormalizeCpRelationType(
_firstNonBlankString([
invite?.relationType,
data["relationType"]?.toString(),
data["relation_type"]?.toString(),
content["relationType"]?.toString(),
content["relation_type"]?.toString(),
], fallback: "CP"),
);
}
Map<String, dynamic> _cpContentMap(Map<String, dynamic> data) { Map<String, dynamic> _cpContentMap(Map<String, dynamic> data) {
final content = data["content"]; final content = data["content"];
try { try {

View File

@ -458,7 +458,7 @@ class _MessageItem extends StatelessWidget {
showMsgItemMenu(ct, ""); showMsgItemMenu(ct, "");
}, },
onTap: () { onTap: () {
_showCpInviteDialogFromMessage(data, cpInvite, actionState); _openCpInviteDialogFromMessage(data, cpInvite, actionState);
}, },
child: Container( child: Container(
width: 251.w, width: 251.w,
@ -529,6 +529,30 @@ class _MessageItem extends StatelessWidget {
); );
} }
void _openCpInviteDialogFromMessage(
Map<String, dynamic> data,
SCSystemInvitMessageRes? cpInvite,
RoomCpInviteActionState actionState,
) {
unawaited(_openCpInviteDialogFromMessageAsync(data, cpInvite, actionState));
}
Future<void> _openCpInviteDialogFromMessageAsync(
Map<String, dynamic> data,
SCSystemInvitMessageRes? cpInvite,
RoomCpInviteActionState actionState,
) async {
final latestState = await _latestCpInviteStateForMessage(data);
if (!context.mounted) {
return;
}
final resolvedActionState =
latestState == _CpInviteMessageState.pending
? actionState
: _cpInviteActionState(latestState);
_showCpInviteDialogFromMessage(data, cpInvite, resolvedActionState);
}
Widget _buildCpInviteGiftThumb(String giftCover) { Widget _buildCpInviteGiftThumb(String giftCover) {
final child = final child =
giftCover.isNotEmpty giftCover.isNotEmpty
@ -788,6 +812,7 @@ class _MessageItem extends StatelessWidget {
applyId, applyId,
SCSysytemMessageType.CP_BUILD.name, SCSysytemMessageType.CP_BUILD.name,
cpInvite: cpInvite, cpInvite: cpInvite,
cpData: data,
) )
: null, : null,
onReject: onReject:
@ -798,6 +823,7 @@ class _MessageItem extends StatelessWidget {
applyId, applyId,
SCSysytemMessageType.CP_BUILD.name, SCSysytemMessageType.CP_BUILD.name,
cpInvite: cpInvite, cpInvite: cpInvite,
cpData: data,
) )
: null, : null,
); );
@ -856,6 +882,56 @@ class _MessageItem extends StatelessWidget {
: _CpInviteMessageState.pending; : _CpInviteMessageState.pending;
} }
Future<_CpInviteMessageState> _latestCpInviteStateForMessage(
Map<String, dynamic> data,
) async {
final localState = _cpInviteStateForMessage(data);
if (localState != _CpInviteMessageState.pending) {
return localState;
}
final applyId = _cpApplyIdFromData(data);
if (applyId.isEmpty) {
return localState;
}
try {
final status = await SCAccountRepository().cpRelationshipApplyStatus(
applyId,
);
final backendState = _cpInviteStateFromBackendStatus(status);
if (backendState != null) {
return backendState;
}
} catch (_) {}
return _cpInviteSecondsLeft(data) <= 0
? _CpInviteMessageState.expired
: localState;
}
_CpInviteMessageState? _cpInviteStateFromBackendStatus(String? status) {
final text = status?.trim().toUpperCase() ?? "";
if (text.isEmpty || text == "WAIT" || text == "WAITING") {
return text.isEmpty ? null : _CpInviteMessageState.pending;
}
if (text.contains("EXPIRE") ||
text.contains("TIMEOUT") ||
text.contains("OVERDUE")) {
return _CpInviteMessageState.expired;
}
if (text.contains("REFUSE") ||
text.contains("REJECT") ||
text.contains("DECLIN") ||
text.contains("DENY")) {
return _CpInviteMessageState.rejected;
}
if (text.contains("AGREE") ||
text.contains("ACCEPT") ||
text.contains("APPROVE") ||
text.contains("ESTABLISH")) {
return _CpInviteMessageState.accepted;
}
return null;
}
_CpInviteMessageState _cpInviteStateFromConversation( _CpInviteMessageState _cpInviteStateFromConversation(
Map<String, dynamic> currentData, Map<String, dynamic> currentData,
) { ) {
@ -1863,7 +1939,14 @@ class _MessageItem extends StatelessWidget {
Future<void> _acceptCpInviteDirect( Future<void> _acceptCpInviteDirect(
String id, String id,
SCSystemInvitMessageRes? cpInvite, SCSystemInvitMessageRes? cpInvite,
Map<String, dynamic>? cpData,
) async { ) async {
if (!await _shouldProcessCpInvite(id, cpInvite, cpData)) {
return;
}
if (!context.mounted) {
return;
}
final profileManager = Provider.of<SocialChatUserProfileManager>( final profileManager = Provider.of<SocialChatUserProfileManager>(
context, context,
listen: false, listen: false,
@ -1882,7 +1965,14 @@ class _MessageItem extends StatelessWidget {
Future<void> _rejectCpInviteDirect( Future<void> _rejectCpInviteDirect(
String id, String id,
SCSystemInvitMessageRes? cpInvite, SCSystemInvitMessageRes? cpInvite,
Map<String, dynamic>? cpData,
) async { ) async {
if (!await _shouldProcessCpInvite(id, cpInvite, cpData)) {
return;
}
if (!context.mounted) {
return;
}
final profileManager = Provider.of<SocialChatUserProfileManager>( final profileManager = Provider.of<SocialChatUserProfileManager>(
context, context,
listen: false, listen: false,
@ -1898,9 +1988,62 @@ class _MessageItem extends StatelessWidget {
} }
} }
void acceptOpt(String id, String type, {SCSystemInvitMessageRes? cpInvite}) { Future<bool> _shouldProcessCpInvite(
String applyId,
SCSystemInvitMessageRes? cpInvite,
Map<String, dynamic>? data,
) async {
final latestState =
data == null
? await _latestCpInviteStateForApplyId(applyId)
: await _latestCpInviteStateForMessage(data);
if (latestState == _CpInviteMessageState.pending) {
return true;
}
if (!context.mounted) {
return false;
}
if (data != null) {
_showCpInviteDialogFromMessage(
data,
cpInvite,
_cpInviteActionState(latestState),
);
} else {
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
if (latestState == _CpInviteMessageState.expired) {
SCTts.show(SCAppLocalizations.of(context)!.closeFriendInviteTimedOut);
}
}
updateCall();
return false;
}
Future<_CpInviteMessageState> _latestCpInviteStateForApplyId(
String applyId,
) async {
if (applyId.trim().isEmpty) {
return _CpInviteMessageState.pending;
}
try {
final status = await SCAccountRepository().cpRelationshipApplyStatus(
applyId,
);
return _cpInviteStateFromBackendStatus(status) ??
_CpInviteMessageState.pending;
} catch (_) {
return _CpInviteMessageState.pending;
}
}
void acceptOpt(
String id,
String type, {
SCSystemInvitMessageRes? cpInvite,
Map<String, dynamic>? cpData,
}) {
if (type == SCSysytemMessageType.CP_BUILD.name) { if (type == SCSysytemMessageType.CP_BUILD.name) {
unawaited(_acceptCpInviteDirect(id, cpInvite)); unawaited(_acceptCpInviteDirect(id, cpInvite, cpData));
return; return;
} }
SmartDialog.show( SmartDialog.show(
@ -1948,9 +2091,14 @@ class _MessageItem extends StatelessWidget {
); );
} }
void rejectOpt(String id, String type, {SCSystemInvitMessageRes? cpInvite}) { void rejectOpt(
String id,
String type, {
SCSystemInvitMessageRes? cpInvite,
Map<String, dynamic>? cpData,
}) {
if (type == SCSysytemMessageType.CP_BUILD.name) { if (type == SCSysytemMessageType.CP_BUILD.name) {
unawaited(_rejectCpInviteDirect(id, cpInvite)); unawaited(_rejectCpInviteDirect(id, cpInvite, cpData));
return; return;
} }
SmartDialog.show( SmartDialog.show(

View File

@ -434,8 +434,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
.addConversationListener( .addConversationListener(
listener: V2TimConversationListener( listener: V2TimConversationListener(
onNewConversation: (conversationList) { onNewConversation: (conversationList) {
// _onRefreshConversation(conversationList); _onRefreshConversation(conversationList);
initConversation(); },
onConversationChanged: (conversationList) {
_onRefreshConversation(conversationList);
}, },
onTotalUnreadMessageCountChanged: (int totalUnreadCount) { onTotalUnreadMessageCountChanged: (int totalUnreadCount) {
messageUnReadCount = totalUnreadCount; messageUnReadCount = totalUnreadCount;
@ -605,7 +607,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
conversationMap.clear(); conversationMap.clear();
if (conversationList != null) { if (conversationList != null) {
for (V2TimConversation? conversation in conversationList) { for (V2TimConversation? conversation in conversationList) {
conversationMap[conversation!.conversationID] = conversation; if (conversation == null) {
continue;
}
conversationMap[conversation.conversationID] = conversation;
_handleCpBuildFromConversation(conversation);
} }
} }
getConversationList(); getConversationList();
@ -853,6 +859,13 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (data == null) { if (data == null) {
return null; return null;
} }
final relationNoticePeerId = _cpRelationNoticePeerIdFromData(
data,
message: message,
);
if (relationNoticePeerId != null) {
return relationNoticePeerId;
}
final invite = _cpInviteFromMessageData(data); final invite = _cpInviteFromMessageData(data);
final currentProfile = AccountStorage().getCurrentUser()?.userProfile; final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
if (invite == null || currentProfile == null) { if (invite == null || currentProfile == null) {
@ -930,6 +943,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (_handleCpInviteResultMessage(data, msgId: message?.msgID)) { if (_handleCpInviteResultMessage(data, msgId: message?.msgID)) {
return; return;
} }
if (_handleCpRelationRefreshNoticeMessage(data, message: message)) {
return;
}
if (noticeType != SCSysytemMessageType.CP_BUILD.name) { if (noticeType != SCSysytemMessageType.CP_BUILD.name) {
return; return;
} }
@ -1241,6 +1257,47 @@ class RealTimeMessagingManager extends ChangeNotifier {
return true; return true;
} }
bool _handleCpRelationRefreshNoticeMessage(
Map<String, dynamic> data, {
V2TimMessage? message,
}) {
final noticeType = _cpRelationNoticeTypeFromData(data);
if (!_isCpRelationRefreshNoticeType(noticeType)) {
return false;
}
_cpInviteLog(
'handle CP relation refresh notice noticeType=$noticeType raw=$data',
);
final context = navigatorKey.currentState?.context;
if (context != null) {
unawaited(
Provider.of<SocialChatUserProfileManager>(
context,
listen: false,
).refreshLoadedCpProfiles(),
);
if (noticeType == SCSysytemMessageType.CP_DISMISS.name) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
final peerUserId = _cpRelationNoticePeerIdFromData(
data,
message: message,
);
if (currentUserId.isNotEmpty && peerUserId != null) {
Provider.of<RtcProvider?>(
context,
listen: false,
)?.invalidateRoomMicRelationBetween(
userId: currentUserId,
peerUserId: peerUserId,
relationType: _cpRelationTypeFromNoticeData(data),
);
}
}
}
return true;
}
Future<void> _showCpInviteResultDialog({ Future<void> _showCpInviteResultDialog({
required BuildContext context, required BuildContext context,
required Map<String, dynamic> data, required Map<String, dynamic> data,
@ -2039,13 +2096,88 @@ class RealTimeMessagingManager extends ChangeNotifier {
content["type"]?.toString(), content["type"]?.toString(),
])?.toUpperCase() ?? ])?.toUpperCase() ??
""; "";
if (noticeType == SCSysytemMessageType.CP_BUILD.name) { if (noticeType == SCSysytemMessageType.CP_BUILD.name ||
_isCpRelationRefreshNoticeType(noticeType)) {
return true; return true;
} }
return _cpInviteApplyIdFromData(data).isNotEmpty && return _cpInviteApplyIdFromData(data).isNotEmpty &&
_cpInviteResultFromMessageData(data) != null; _cpInviteResultFromMessageData(data) != null;
} }
String _cpRelationNoticeTypeFromData(Map<String, dynamic> data) {
final content = _cpInviteContentMap(data);
return (_firstCpInviteValue([
data["noticeType"]?.toString(),
data["type"]?.toString(),
content["noticeType"]?.toString(),
content["type"]?.toString(),
]) ??
"")
.toUpperCase();
}
bool _isCpRelationRefreshNoticeType(String noticeType) {
return noticeType == SCSysytemMessageType.CP_DISMISS.name ||
noticeType == SCSysytemMessageType.CP_BLESS.name;
}
String _cpRelationTypeFromNoticeData(Map<String, dynamic> data) {
final content = _cpInviteContentMap(data);
return scNormalizeCpRelationType(
_firstCpInviteValue([
data["relationType"]?.toString(),
data["relation_type"]?.toString(),
content["relationType"]?.toString(),
content["relation_type"]?.toString(),
]) ??
"CP",
);
}
String? _cpRelationNoticePeerIdFromData(
Map<String, dynamic> data, {
V2TimMessage? message,
}) {
if (!_isCpRelationRefreshNoticeType(_cpRelationNoticeTypeFromData(data))) {
return null;
}
final content = _cpInviteContentMap(data);
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
final currentIds =
[currentProfile?.id, currentProfile?.account]
.map((value) => value?.trim() ?? "")
.where((value) => value.isNotEmpty)
.toSet();
final candidates = <String?>[
data["expand"]?.toString(),
content["expand"]?.toString(),
data["peerUserId"]?.toString(),
data["cpUserId"]?.toString(),
data["targetUserId"]?.toString(),
data["senderUserId"]?.toString(),
data["fromUserId"]?.toString(),
data["userId"]?.toString(),
content["peerUserId"]?.toString(),
content["cpUserId"]?.toString(),
content["targetUserId"]?.toString(),
content["senderUserId"]?.toString(),
content["fromUserId"]?.toString(),
content["userId"]?.toString(),
message?.sender,
message?.userID,
];
for (final candidate in candidates) {
final userId = candidate?.trim() ?? "";
if (userId.isEmpty ||
currentIds.contains(userId) ||
!scLooksLikeBackendUserId(userId)) {
continue;
}
return userId;
}
return null;
}
void _cpInviteLog(String message) { void _cpInviteLog(String message) {
debugPrint('[CP][Invite] $message'); debugPrint('[CP][Invite] $message');
} }
@ -2053,10 +2185,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
void _onRefreshConversation(List<V2TimConversation> conversations) { void _onRefreshConversation(List<V2TimConversation> conversations) {
for (V2TimConversation conversation in conversations) { for (V2TimConversation conversation in conversations) {
conversationMap[conversation.conversationID] = conversation; conversationMap[conversation.conversationID] = conversation;
_handleCpBuildFromConversation(conversation);
} }
getConversationList(); getConversationList();
} }
void _handleCpBuildFromConversation(V2TimConversation conversation) {
final message = conversation.lastMessage;
if (message == null || message.groupID != null) {
return;
}
final data = _decodeCpSystemMessageData(message);
if (!_isCpRelationInviteNoticeData(data)) {
return;
}
unawaited(_handleIncomingCpBuildMessage(message));
}
///im群聊 ///im群聊
Future<V2TimValueCallback<String>> createRoomGroup( Future<V2TimValueCallback<String>> createRoomGroup(
String groupID, String groupID,

View File

@ -178,22 +178,28 @@ class SocialChatUserProfileManager extends ChangeNotifier {
if (userId.trim().isEmpty) { if (userId.trim().isEmpty) {
return profile; return profile;
} }
final cpPairs = <CPRes>[]; final fallbackCpPairs = <CPRes>[];
final closeFriendPairs = <CPRes>[]; final fallbackCloseFriendPairs = <CPRes>[];
_addCpRelationPairs( _addCpRelationPairs(
cpPairs: cpPairs, cpPairs: fallbackCpPairs,
closeFriendPairs: closeFriendPairs, closeFriendPairs: fallbackCloseFriendPairs,
relations: profile.cpList, relations: profile.cpList,
fallbackRelationType: 'CP', fallbackRelationType: 'CP',
); );
_addCpRelationPairs( _addCpRelationPairs(
cpPairs: cpPairs, cpPairs: fallbackCpPairs,
closeFriendPairs: closeFriendPairs, closeFriendPairs: fallbackCloseFriendPairs,
relations: profile.closeFriendList, relations: profile.closeFriendList,
); );
final cpPairs = <CPRes>[];
final closeFriendPairs = <CPRes>[];
var loadedAnyCabin = false;
await Future.wait( await Future.wait(
_cpProfileRelationTypes.map((relationType) async { _cpProfileRelationTypes.map((relationType) async {
final cabin = await _safeLoadCpCabin(userId, relationType); final cabin = await _safeLoadCpCabin(userId, relationType);
if (cabin != null) {
loadedAnyCabin = true;
}
final cpPair = cabin?.cpPairUserProfileCO; final cpPair = cabin?.cpPairUserProfileCO;
if (cpPair == null || !_hasCpPair(cpPair)) { if (cpPair == null || !_hasCpPair(cpPair)) {
return; return;
@ -231,6 +237,12 @@ class SocialChatUserProfileManager extends ChangeNotifier {
'closeFriends=${closeFriendPairs.length}', 'closeFriends=${closeFriendPairs.length}',
); );
} }
if (!loadedAnyCabin) {
return profile.copyWith(
cpList: fallbackCpPairs,
closeFriendList: fallbackCloseFriendPairs,
);
}
return profile.copyWith( return profile.copyWith(
cpList: cpPairs, cpList: cpPairs,
closeFriendList: closeFriendPairs, closeFriendList: closeFriendPairs,