飘屏动画
This commit is contained in:
parent
cbb811a18a
commit
71993f438b
@ -368,7 +368,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
}
|
}
|
||||||
SCLoadingManager.show();
|
SCLoadingManager.show();
|
||||||
Future.wait([
|
Future.wait([
|
||||||
SCAccountRepository().loadUserInfo(userId),
|
SCAccountRepository().loadUserInfo(userId, silentErrorToast: true),
|
||||||
SCAccountRepository().friendRelationCheck(userId),
|
SCAccountRepository().friendRelationCheck(userId),
|
||||||
])
|
])
|
||||||
.then((result) {
|
.then((result) {
|
||||||
@ -593,6 +593,7 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
(c, i) => _MessageItem(
|
(c, i) => _MessageItem(
|
||||||
message: currentConversationMessageList[i],
|
message: currentConversationMessageList[i],
|
||||||
selfVipChatBubble: _selfVipChatBubble,
|
selfVipChatBubble: _selfVipChatBubble,
|
||||||
|
currentConversation: currentConversation,
|
||||||
preMessage:
|
preMessage:
|
||||||
i > 0 ? currentConversationMessageList[i - 1] : null,
|
i > 0 ? currentConversationMessageList[i - 1] : null,
|
||||||
isSystem:
|
isSystem:
|
||||||
@ -1164,6 +1165,7 @@ class _MessageItem extends StatelessWidget {
|
|||||||
final V2TimMessage message;
|
final V2TimMessage message;
|
||||||
final V2TimMessage? preMessage;
|
final V2TimMessage? preMessage;
|
||||||
final SCVipResourceRes? selfVipChatBubble;
|
final SCVipResourceRes? selfVipChatBubble;
|
||||||
|
final V2TimConversation? currentConversation;
|
||||||
|
|
||||||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||||||
|
|
||||||
@ -1179,6 +1181,7 @@ class _MessageItem extends StatelessWidget {
|
|||||||
_MessageItem({
|
_MessageItem({
|
||||||
required this.message,
|
required this.message,
|
||||||
this.selfVipChatBubble,
|
this.selfVipChatBubble,
|
||||||
|
this.currentConversation,
|
||||||
this.preMessage,
|
this.preMessage,
|
||||||
// this.userProfile,
|
// this.userProfile,
|
||||||
this.isSystem = false,
|
this.isSystem = false,
|
||||||
@ -1529,12 +1532,15 @@ class _MessageItem extends StatelessWidget {
|
|||||||
content["acceptUserNickname"]?.toString(),
|
content["acceptUserNickname"]?.toString(),
|
||||||
content["receiverNickname"]?.toString(),
|
content["receiverNickname"]?.toString(),
|
||||||
content["receiverUserNickname"]?.toString(),
|
content["receiverUserNickname"]?.toString(),
|
||||||
|
friend?.userNickname,
|
||||||
|
currentConversation?.showName,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
account: _firstNonBlankString([
|
account: _firstNonBlankString([
|
||||||
data["acceptAccount"]?.toString(),
|
data["acceptAccount"]?.toString(),
|
||||||
data["receiverAccount"]?.toString(),
|
data["receiverAccount"]?.toString(),
|
||||||
content["acceptAccount"]?.toString(),
|
content["acceptAccount"]?.toString(),
|
||||||
content["receiverAccount"]?.toString(),
|
content["receiverAccount"]?.toString(),
|
||||||
|
friend?.account,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
actualAccount: _firstNonBlankString([
|
actualAccount: _firstNonBlankString([
|
||||||
data["acceptActualAccount"]?.toString(),
|
data["acceptActualAccount"]?.toString(),
|
||||||
@ -1545,8 +1551,10 @@ class _MessageItem extends StatelessWidget {
|
|||||||
content["acceptUserId"]?.toString(),
|
content["acceptUserId"]?.toString(),
|
||||||
content["receiverActualAccount"]?.toString(),
|
content["receiverActualAccount"]?.toString(),
|
||||||
content["receiverUserId"]?.toString(),
|
content["receiverUserId"]?.toString(),
|
||||||
|
friend?.id,
|
||||||
|
currentConversation?.userID,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
fallback: currentName,
|
fallback: currentConversation?.userID ?? "User",
|
||||||
);
|
);
|
||||||
final receiverAvatar = _firstNonBlankString([
|
final receiverAvatar = _firstNonBlankString([
|
||||||
data["acceptUserAvatar"]?.toString(),
|
data["acceptUserAvatar"]?.toString(),
|
||||||
@ -1555,7 +1563,9 @@ class _MessageItem extends StatelessWidget {
|
|||||||
content["acceptUserAvatar"]?.toString(),
|
content["acceptUserAvatar"]?.toString(),
|
||||||
content["receiverAvatar"]?.toString(),
|
content["receiverAvatar"]?.toString(),
|
||||||
content["receiverUserAvatar"]?.toString(),
|
content["receiverUserAvatar"]?.toString(),
|
||||||
], fallback: currentProfile?.userAvatar ?? "");
|
friend?.userAvatar,
|
||||||
|
currentConversation?.faceUrl,
|
||||||
|
], fallback: "");
|
||||||
final isInviter = _isCurrentUserCpInviter(data);
|
final isInviter = _isCurrentUserCpInviter(data);
|
||||||
final relationType = scNormalizeCpRelationType(
|
final relationType = scNormalizeCpRelationType(
|
||||||
_firstNonBlankString([
|
_firstNonBlankString([
|
||||||
@ -1579,12 +1589,18 @@ class _MessageItem extends StatelessWidget {
|
|||||||
rightName: isInviter ? receiverName : currentName,
|
rightName: isInviter ? receiverName : currentName,
|
||||||
leftAvatar:
|
leftAvatar:
|
||||||
isInviter
|
isInviter
|
||||||
? currentProfile?.userAvatar ?? ""
|
? _firstNonBlankString([
|
||||||
|
currentProfile?.userAvatar,
|
||||||
|
inviterAvatar,
|
||||||
|
])
|
||||||
: inviterAvatar,
|
: inviterAvatar,
|
||||||
rightAvatar:
|
rightAvatar:
|
||||||
isInviter
|
isInviter
|
||||||
? receiverAvatar
|
? receiverAvatar
|
||||||
: currentProfile?.userAvatar ?? "",
|
: _firstNonBlankString([
|
||||||
|
currentProfile?.userAvatar,
|
||||||
|
receiverAvatar,
|
||||||
|
]),
|
||||||
relationType: relationType,
|
relationType: relationType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1631,26 +1647,73 @@ class _MessageItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
final isInviter = _isCurrentUserCpInviter(data);
|
final isInviter = _isCurrentUserCpInviter(data);
|
||||||
final content = _cpContentMap(data);
|
final content = _cpContentMap(data);
|
||||||
|
final receiverAvatar = _firstNonBlankString([
|
||||||
|
data["acceptUserAvatar"]?.toString(),
|
||||||
|
data["receiverAvatar"]?.toString(),
|
||||||
|
data["receiverUserAvatar"]?.toString(),
|
||||||
|
content["acceptUserAvatar"]?.toString(),
|
||||||
|
content["receiverAvatar"]?.toString(),
|
||||||
|
content["receiverUserAvatar"]?.toString(),
|
||||||
|
friend?.userAvatar,
|
||||||
|
currentConversation?.faceUrl,
|
||||||
|
]);
|
||||||
|
final receiverHeaddress = _firstNonBlankString([
|
||||||
|
data["acceptHeaddress"]?.toString(),
|
||||||
|
data["acceptAvatarFrame"]?.toString(),
|
||||||
|
data["receiverHeaddress"]?.toString(),
|
||||||
|
data["receiverAvatarFrame"]?.toString(),
|
||||||
|
content["acceptHeaddress"]?.toString(),
|
||||||
|
content["acceptAvatarFrame"]?.toString(),
|
||||||
|
content["receiverHeaddress"]?.toString(),
|
||||||
|
content["receiverAvatarFrame"]?.toString(),
|
||||||
|
friend?.getHeaddress()?.sourceUrl,
|
||||||
|
]);
|
||||||
|
final receiverHeaddressCover = _firstNonBlankString([
|
||||||
|
data["acceptHeaddressCover"]?.toString(),
|
||||||
|
data["acceptAvatarFrameCover"]?.toString(),
|
||||||
|
data["receiverHeaddressCover"]?.toString(),
|
||||||
|
data["receiverAvatarFrameCover"]?.toString(),
|
||||||
|
content["acceptHeaddressCover"]?.toString(),
|
||||||
|
content["acceptAvatarFrameCover"]?.toString(),
|
||||||
|
content["receiverHeaddressCover"]?.toString(),
|
||||||
|
content["receiverAvatarFrameCover"]?.toString(),
|
||||||
|
friend?.getHeaddress()?.cover,
|
||||||
|
]);
|
||||||
final receiverName =
|
final receiverName =
|
||||||
isInviter
|
isInviter
|
||||||
? _cpDisplayName(
|
? _cpDisplayName(
|
||||||
nickname: _firstNonBlankString([
|
nickname: _firstNonBlankString([
|
||||||
data["acceptNickname"]?.toString(),
|
data["acceptNickname"]?.toString(),
|
||||||
data["acceptUserNickname"]?.toString(),
|
data["acceptUserNickname"]?.toString(),
|
||||||
|
data["receiverNickname"]?.toString(),
|
||||||
|
data["receiverUserNickname"]?.toString(),
|
||||||
content["acceptNickname"]?.toString(),
|
content["acceptNickname"]?.toString(),
|
||||||
content["acceptUserNickname"]?.toString(),
|
content["acceptUserNickname"]?.toString(),
|
||||||
|
content["receiverNickname"]?.toString(),
|
||||||
|
content["receiverUserNickname"]?.toString(),
|
||||||
|
friend?.userNickname,
|
||||||
|
currentConversation?.showName,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
account: _firstNonBlankString([
|
account: _firstNonBlankString([
|
||||||
data["acceptAccount"]?.toString(),
|
data["acceptAccount"]?.toString(),
|
||||||
|
data["receiverAccount"]?.toString(),
|
||||||
content["acceptAccount"]?.toString(),
|
content["acceptAccount"]?.toString(),
|
||||||
|
content["receiverAccount"]?.toString(),
|
||||||
|
friend?.account,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
actualAccount: _firstNonBlankString([
|
actualAccount: _firstNonBlankString([
|
||||||
data["acceptActualAccount"]?.toString(),
|
data["acceptActualAccount"]?.toString(),
|
||||||
content["acceptActualAccount"]?.toString(),
|
content["acceptActualAccount"]?.toString(),
|
||||||
data["acceptUserId"]?.toString(),
|
data["acceptUserId"]?.toString(),
|
||||||
content["acceptUserId"]?.toString(),
|
content["acceptUserId"]?.toString(),
|
||||||
|
data["receiverActualAccount"]?.toString(),
|
||||||
|
data["receiverUserId"]?.toString(),
|
||||||
|
content["receiverActualAccount"]?.toString(),
|
||||||
|
content["receiverUserId"]?.toString(),
|
||||||
|
friend?.id,
|
||||||
|
currentConversation?.userID,
|
||||||
], fallback: ""),
|
], fallback: ""),
|
||||||
fallback: "User",
|
fallback: currentConversation?.userID ?? "User",
|
||||||
)
|
)
|
||||||
: currentName;
|
: currentName;
|
||||||
final relationType = scNormalizeCpRelationType(cpInvite?.relationType);
|
final relationType = scNormalizeCpRelationType(cpInvite?.relationType);
|
||||||
@ -1685,29 +1748,25 @@ class _MessageItem extends StatelessWidget {
|
|||||||
name: receiverName,
|
name: receiverName,
|
||||||
avatarUrl:
|
avatarUrl:
|
||||||
isInviter
|
isInviter
|
||||||
? _firstNonBlankString([
|
? receiverAvatar
|
||||||
data["acceptUserAvatar"]?.toString(),
|
: _firstNonBlankString([
|
||||||
content["acceptUserAvatar"]?.toString(),
|
currentProfile?.userAvatar,
|
||||||
], fallback: "")
|
receiverAvatar,
|
||||||
: currentProfile?.userAvatar ?? "",
|
]),
|
||||||
headdress:
|
headdress:
|
||||||
isInviter
|
isInviter
|
||||||
? _firstNonBlankString([
|
? receiverHeaddress
|
||||||
data["acceptHeaddress"]?.toString(),
|
: _firstNonBlankString([
|
||||||
data["acceptAvatarFrame"]?.toString(),
|
currentProfile?.getHeaddress()?.sourceUrl,
|
||||||
content["acceptHeaddress"]?.toString(),
|
receiverHeaddress,
|
||||||
content["acceptAvatarFrame"]?.toString(),
|
]),
|
||||||
], fallback: "")
|
|
||||||
: currentProfile?.getHeaddress()?.sourceUrl ?? "",
|
|
||||||
headdressCover:
|
headdressCover:
|
||||||
isInviter
|
isInviter
|
||||||
? _firstNonBlankString([
|
? receiverHeaddressCover
|
||||||
data["acceptHeaddressCover"]?.toString(),
|
: _firstNonBlankString([
|
||||||
data["acceptAvatarFrameCover"]?.toString(),
|
currentProfile?.getHeaddress()?.cover,
|
||||||
content["acceptHeaddressCover"]?.toString(),
|
receiverHeaddressCover,
|
||||||
content["acceptAvatarFrameCover"]?.toString(),
|
]),
|
||||||
], fallback: "")
|
|
||||||
: currentProfile?.getHeaddress()?.cover ?? "",
|
|
||||||
),
|
),
|
||||||
style:
|
style:
|
||||||
isInviter
|
isInviter
|
||||||
@ -1934,23 +1993,91 @@ class _MessageItem extends StatelessWidget {
|
|||||||
if (currentProfile == null) {
|
if (currentProfile == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
final currentIds = _cpCurrentProfileIds(currentProfile);
|
||||||
|
if (currentIds.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final receiverIds = _cpInviteReceiverIdentityValues(data);
|
||||||
|
if (currentIds.any(receiverIds.contains)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final inviterIds = _cpInviteInviterIdentityValues(data);
|
||||||
|
return currentIds.any(inviterIds.contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpCurrentProfileIds(SocialChatUserProfile currentProfile) {
|
||||||
|
return [currentProfile.id, currentProfile.account]
|
||||||
|
.map((value) => value?.trim() ?? "")
|
||||||
|
.where((value) => value.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
|
||||||
final content = _cpContentMap(data);
|
final content = _cpContentMap(data);
|
||||||
final inviterIds =
|
final values = <String?>[
|
||||||
<String>[
|
data["account"]?.toString(),
|
||||||
data["applyUserId"]?.toString() ?? "",
|
data["actualAccount"]?.toString(),
|
||||||
data["applyUserAccount"]?.toString() ?? "",
|
data["applyUserId"]?.toString(),
|
||||||
data["account"]?.toString() ?? "",
|
data["applyUserAccount"]?.toString(),
|
||||||
data["actualAccount"]?.toString() ?? "",
|
data["applyActualAccount"]?.toString(),
|
||||||
content["applyUserId"]?.toString() ?? "",
|
data["applyUserActualAccount"]?.toString(),
|
||||||
content["applyUserAccount"]?.toString() ?? "",
|
data["inviterActualAccount"]?.toString(),
|
||||||
content["account"]?.toString() ?? "",
|
data["inviterUserId"]?.toString(),
|
||||||
content["actualAccount"]?.toString() ?? "",
|
data["inviterId"]?.toString(),
|
||||||
]
|
data["senderUserId"]?.toString(),
|
||||||
.map((value) => value.trim())
|
data["senderId"]?.toString(),
|
||||||
.where((value) => value.isNotEmpty)
|
data["fromUserId"]?.toString(),
|
||||||
.toSet();
|
content["applyUserId"]?.toString(),
|
||||||
return inviterIds.contains(currentProfile.id?.trim() ?? "") ||
|
content["applyUserAccount"]?.toString(),
|
||||||
inviterIds.contains(currentProfile.account?.trim() ?? "");
|
content["applyActualAccount"]?.toString(),
|
||||||
|
content["applyUserActualAccount"]?.toString(),
|
||||||
|
content["inviterActualAccount"]?.toString(),
|
||||||
|
content["inviterUserId"]?.toString(),
|
||||||
|
content["inviterId"]?.toString(),
|
||||||
|
content["senderUserId"]?.toString(),
|
||||||
|
content["senderId"]?.toString(),
|
||||||
|
content["fromUserId"]?.toString(),
|
||||||
|
];
|
||||||
|
final explicitValues =
|
||||||
|
values.map((value) => value?.trim() ?? "").where((value) {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
|
if (explicitValues.isNotEmpty) {
|
||||||
|
return explicitValues;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
content["account"]?.toString(),
|
||||||
|
content["actualAccount"]?.toString(),
|
||||||
|
content["userId"]?.toString(),
|
||||||
|
].map((value) => value?.trim() ?? "").where((value) {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpInviteReceiverIdentityValues(Map<String, dynamic> data) {
|
||||||
|
final content = _cpContentMap(data);
|
||||||
|
return [
|
||||||
|
data["acceptUserId"]?.toString(),
|
||||||
|
data["acceptAccount"]?.toString(),
|
||||||
|
data["acceptActualAccount"]?.toString(),
|
||||||
|
data["receiverId"]?.toString(),
|
||||||
|
data["receiverUserId"]?.toString(),
|
||||||
|
data["receiverAccount"]?.toString(),
|
||||||
|
data["receiverActualAccount"]?.toString(),
|
||||||
|
data["targetUserId"]?.toString(),
|
||||||
|
data["toUserId"]?.toString(),
|
||||||
|
content["acceptUserId"]?.toString(),
|
||||||
|
content["acceptAccount"]?.toString(),
|
||||||
|
content["acceptActualAccount"]?.toString(),
|
||||||
|
content["receiverId"]?.toString(),
|
||||||
|
content["receiverUserId"]?.toString(),
|
||||||
|
content["receiverAccount"]?.toString(),
|
||||||
|
content["receiverActualAccount"]?.toString(),
|
||||||
|
content["targetUserId"]?.toString(),
|
||||||
|
content["toUserId"]?.toString(),
|
||||||
|
].map((value) => value?.trim() ?? "").where((value) {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
int _cpInviteSecondsLeft(Map<String, dynamic> data) {
|
int _cpInviteSecondsLeft(Map<String, dynamic> data) {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository
|
|||||||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_effect_scheduler.dart';
|
import 'package:yumi/shared/tools/sc_room_effect_scheduler.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
|
||||||
|
|
||||||
enum _CpGuideTab { note, rights }
|
enum _CpGuideTab { note, rights }
|
||||||
|
|
||||||
@ -593,7 +592,6 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
? "Lv.$levelNumber"
|
? "Lv.$levelNumber"
|
||||||
: "Lv.${rows.length + 1}"),
|
: "Lv.${rows.length + 1}"),
|
||||||
intimacy: _formatRightsIntimacyValue(requiredValue),
|
intimacy: _formatRightsIntimacyValue(requiredValue),
|
||||||
rights: level.rights.map(_CpRightItem.fromRes).toList(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -630,23 +628,16 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRightsTable(List<_CpRightsLevel> rows) {
|
Widget _buildRightsTable(List<_CpRightsLevel> rows) {
|
||||||
|
const tableWidth = 351.0;
|
||||||
|
const columnWidth = tableWidth / 2;
|
||||||
const headerHeight = 39.0;
|
const headerHeight = 39.0;
|
||||||
const rightCellHeight = 77.0;
|
const rowHeight = 77.0;
|
||||||
const dividerHeight = 1.0;
|
final tableHeight = headerHeight + rows.length * rowHeight;
|
||||||
final tableHeight =
|
|
||||||
headerHeight +
|
|
||||||
rows.fold<double>(
|
|
||||||
0,
|
|
||||||
(height, row) =>
|
|
||||||
height +
|
|
||||||
row.rightSlots * rightCellHeight +
|
|
||||||
(row.rightSlots - 1) * dividerHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 351.w,
|
width: tableWidth.w,
|
||||||
height: tableHeight.w,
|
height: tableHeight.w,
|
||||||
color: Colors.white.withValues(alpha: 0.3),
|
color: Colors.white.withValues(alpha: 0.3),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@ -654,15 +645,15 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
Positioned(
|
Positioned(
|
||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 117.w,
|
left: columnWidth.w,
|
||||||
width: 117.w,
|
width: columnWidth.w,
|
||||||
child: ColoredBox(color: Colors.white.withValues(alpha: 0.24)),
|
child: ColoredBox(color: Colors.white.withValues(alpha: 0.24)),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
top: headerHeight.w,
|
top: headerHeight.w,
|
||||||
child: _tableLine(width: 351),
|
child: _tableLine(width: tableWidth),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -671,9 +662,8 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
height: headerHeight.w,
|
height: headerHeight.w,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_tableHeader("Level"),
|
_tableHeader("Level", columnWidth),
|
||||||
_tableHeader("Intimacy Level"),
|
_tableHeader("Intimacy Level", columnWidth),
|
||||||
_tableHeader("Rights"),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -687,9 +677,9 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _tableHeader(String value) {
|
Widget _tableHeader(String value, double width) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 117.w,
|
width: width.w,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
value,
|
value,
|
||||||
@ -701,41 +691,18 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRightsLevelRow(_CpRightsLevel row) {
|
Widget _buildRightsLevelRow(_CpRightsLevel row) {
|
||||||
const rightCellHeight = 77.0;
|
const tableWidth = 351.0;
|
||||||
const dividerHeight = 1.0;
|
const columnWidth = tableWidth / 2;
|
||||||
final groupHeight =
|
const rowHeight = 77.0;
|
||||||
row.rightSlots * rightCellHeight + (row.rightSlots - 1) * dividerHeight;
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: groupHeight.w,
|
height: rowHeight.w,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_spanCell(row.level, groupHeight),
|
_spanCell(row.level, width: columnWidth, height: rowHeight),
|
||||||
_spanCell(row.intimacy, groupHeight),
|
_spanCell(row.intimacy, width: columnWidth, height: rowHeight),
|
||||||
SizedBox(width: 117.w, height: groupHeight.w),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
width: 117.w,
|
|
||||||
height: groupHeight.w,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
for (var i = 0; i < row.rightSlots; i++) ...[
|
|
||||||
SizedBox(
|
|
||||||
height: rightCellHeight.w,
|
|
||||||
child: Center(
|
|
||||||
child: _buildRightCell(
|
|
||||||
i < row.rights.length ? row.rights[i] : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (i != row.rightSlots - 1) _tableLine(width: 117),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -743,16 +710,20 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: _tableLine(width: 351),
|
child: _tableLine(width: tableWidth),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _spanCell(String value, double height) {
|
Widget _spanCell(
|
||||||
|
String value, {
|
||||||
|
required double width,
|
||||||
|
required double height,
|
||||||
|
}) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 117.w,
|
width: width.w,
|
||||||
height: height.w,
|
height: height.w,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -768,61 +739,6 @@ class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|||||||
return Container(width: width.w, height: 1.w, color: Colors.white);
|
return Container(width: width.w, height: 1.w, color: Colors.white);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRightCell(_CpRightItem? right) {
|
|
||||||
final opacity = (right?.unlocked ?? false) ? 1.0 : 0.55;
|
|
||||||
final title = right?.title.trim() ?? "";
|
|
||||||
return Opacity(
|
|
||||||
opacity: opacity,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
_buildRightIcon(right?.icon ?? ""),
|
|
||||||
if (title.isNotEmpty) ...[
|
|
||||||
SizedBox(height: 4.w),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 6.w),
|
|
||||||
child: Text(
|
|
||||||
title,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(fontSize: 10, lineHeight: 1.1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightIcon(String icon) {
|
|
||||||
if (icon.trim().isNotEmpty) {
|
|
||||||
return ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(9.w),
|
|
||||||
child: netImage(
|
|
||||||
url: icon,
|
|
||||||
width: 42.w,
|
|
||||||
height: 42.w,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
noDefaultImg: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Container(
|
|
||||||
width: 42.w,
|
|
||||||
height: 42.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withValues(alpha: 0.18),
|
|
||||||
borderRadius: BorderRadius.circular(9.w),
|
|
||||||
border: Border.all(
|
|
||||||
color: Colors.white.withValues(alpha: 0.45),
|
|
||||||
width: 1.w,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _rightsIndicatorWidth(String label, TextStyle style) {
|
double _rightsIndicatorWidth(String label, TextStyle style) {
|
||||||
final painter = TextPainter(
|
final painter = TextPainter(
|
||||||
text: TextSpan(text: label, style: style),
|
text: TextSpan(text: label, style: style),
|
||||||
@ -1005,35 +921,8 @@ class _FaqItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CpRightsLevel {
|
class _CpRightsLevel {
|
||||||
const _CpRightsLevel({
|
const _CpRightsLevel({required this.level, required this.intimacy});
|
||||||
required this.level,
|
|
||||||
required this.intimacy,
|
|
||||||
this.rights = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
final String level;
|
final String level;
|
||||||
final String intimacy;
|
final String intimacy;
|
||||||
final List<_CpRightItem> rights;
|
|
||||||
|
|
||||||
int get rightSlots => rights.isEmpty ? 1 : rights.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CpRightItem {
|
|
||||||
const _CpRightItem({
|
|
||||||
required this.icon,
|
|
||||||
required this.title,
|
|
||||||
required this.unlocked,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory _CpRightItem.fromRes(SCCpRightRes right) {
|
|
||||||
return _CpRightItem(
|
|
||||||
icon: right.icon?.trim() ?? "",
|
|
||||||
title: right.title?.trim() ?? "",
|
|
||||||
unlocked: right.unlocked ?? false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String icon;
|
|
||||||
final String title;
|
|
||||||
final bool unlocked;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import 'package:yumi/shared/data_sources/sources/remote/net/api.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
||||||
import 'package:yumi/main.dart';
|
import 'package:yumi/main.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
||||||
@ -34,6 +35,7 @@ import 'package:yumi/services/audio/rtc_manager.dart';
|
|||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/gift/sc_gift_combo_send_button.dart';
|
import 'package:yumi/ui_kit/widgets/gift/sc_gift_combo_send_button.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/room/cp/room_cp_invite_dialog.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||||||
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
||||||
import 'package:yumi/modules/gift/cp_rights/cp_rights_guide_page.dart';
|
import 'package:yumi/modules/gift/cp_rights/cp_rights_guide_page.dart';
|
||||||
@ -1562,6 +1564,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
await _refreshGiftBackpack();
|
await _refreshGiftBackpack();
|
||||||
_refreshCpStateAfterGift(request, profileManager);
|
_refreshCpStateAfterGift(request, profileManager);
|
||||||
|
_showCpGiftWaitingDialog(request);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1626,6 +1629,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
profileManager?.updateBalance(result);
|
profileManager?.updateBalance(result);
|
||||||
_refreshCpStateAfterGift(request, profileManager);
|
_refreshCpStateAfterGift(request, profileManager);
|
||||||
|
_showCpGiftWaitingDialog(request);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final errorMessage =
|
final errorMessage =
|
||||||
request.isBackpackGiftRequest && _isBackpackQuantityError(e)
|
request.isBackpackGiftRequest && _isBackpackQuantityError(e)
|
||||||
@ -1668,6 +1672,73 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
unawaited(profileManager?.refreshLoadedCpProfiles());
|
unawaited(profileManager?.refreshLoadedCpProfiles());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showCpGiftWaitingDialog(_GiftSendRequest request) {
|
||||||
|
if (!_isCpGift(request.gift)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final context = navigatorKey.currentState?.context;
|
||||||
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
|
final receiverProfile =
|
||||||
|
request.acceptUsers.isNotEmpty ? request.acceptUsers.first.user : null;
|
||||||
|
if (context == null || currentProfile == null || receiverProfile == null) {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip local CP waiting dialog reason=missing_profile '
|
||||||
|
'giftId=${request.gift.id} '
|
||||||
|
'acceptUserIds=${request.acceptUserIds.join(",")}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final relationType = scNormalizeCpRelationType(request.cpRelationType);
|
||||||
|
_giftFxLog(
|
||||||
|
'show local CP waiting dialog '
|
||||||
|
'giftId=${request.gift.id} '
|
||||||
|
'relationType=$relationType '
|
||||||
|
'receiverId=${receiverProfile.id}',
|
||||||
|
);
|
||||||
|
unawaited(
|
||||||
|
RoomCpInviteDialog.show(
|
||||||
|
context,
|
||||||
|
inviter: RoomCpInviteDialogUser(
|
||||||
|
name: _cpInviteDisplayName(currentProfile),
|
||||||
|
avatarUrl: currentProfile.userAvatar ?? "",
|
||||||
|
headdress: currentProfile.getHeaddress()?.sourceUrl ?? "",
|
||||||
|
headdressCover: currentProfile.getHeaddress()?.cover ?? "",
|
||||||
|
),
|
||||||
|
receiver: RoomCpInviteDialogUser(
|
||||||
|
name: _cpInviteDisplayName(receiverProfile),
|
||||||
|
avatarUrl: receiverProfile.userAvatar ?? "",
|
||||||
|
headdress: receiverProfile.getHeaddress()?.sourceUrl ?? "",
|
||||||
|
headdressCover: receiverProfile.getHeaddress()?.cover ?? "",
|
||||||
|
),
|
||||||
|
style: RoomCpInviteDialogStyle.waiting,
|
||||||
|
countdownSeconds: 86399,
|
||||||
|
descriptionText: _cpInviteWaitingDescriptionText(relationType),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cpInviteDisplayName(SocialChatUserProfile profile) {
|
||||||
|
for (final value in [profile.userNickname, profile.account, profile.id]) {
|
||||||
|
final text = value?.trim() ?? "";
|
||||||
|
if (text.isNotEmpty) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "User";
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cpInviteWaitingDescriptionText(String relationType) {
|
||||||
|
switch (scNormalizeCpRelationType(relationType)) {
|
||||||
|
case "BROTHER":
|
||||||
|
return "We invite you to become Brother";
|
||||||
|
case "SISTERS":
|
||||||
|
return "We invite you to become Sister";
|
||||||
|
case "CP":
|
||||||
|
default:
|
||||||
|
return "We invite you to become Couple";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<MicRes>> _giveBackpackGiftToRecipients(
|
Future<List<MicRes>> _giveBackpackGiftToRecipients(
|
||||||
SCChatRoomRepository repository,
|
SCChatRoomRepository repository,
|
||||||
_GiftSendRequest request,
|
_GiftSendRequest request,
|
||||||
|
|||||||
@ -295,6 +295,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
Timer? _roomRocketRoomReadyRewardRetryTimer;
|
Timer? _roomRocketRoomReadyRewardRetryTimer;
|
||||||
final Set<String> _shownCpInviteMessageKeys = <String>{};
|
final Set<String> _shownCpInviteMessageKeys = <String>{};
|
||||||
final Set<String> _shownCpInviteResultMessageKeys = <String>{};
|
final Set<String> _shownCpInviteResultMessageKeys = <String>{};
|
||||||
|
final Set<String> _shownCpInviteRejectedDialogKeys = <String>{};
|
||||||
|
final Map<String, int> _recentCpRelationNoticeTimes = <String, int>{};
|
||||||
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
|
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
|
||||||
String? roomRedPacketBroadcastGroupId;
|
String? roomRedPacketBroadcastGroupId;
|
||||||
String? roomRedPacketBroadcastRegionCode;
|
String? roomRedPacketBroadcastRegionCode;
|
||||||
@ -685,7 +687,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}) async {
|
}) async {
|
||||||
final cpData = _decodeCpSystemMessageData(message);
|
final cpData = _decodeCpSystemMessageData(message);
|
||||||
unawaited(_handleIncomingCpBuildMessage(message));
|
unawaited(_handleIncomingCpBuildMessage(message));
|
||||||
final cpPeerUserId = _cpInviteConversationPeerIdFromData(cpData);
|
final cpPeerUserId = _cpInviteConversationPeerIdFromData(
|
||||||
|
cpData,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
final didSyncConversation = _upsertC2CConversationFromMessage(
|
final didSyncConversation = _upsertC2CConversationFromMessage(
|
||||||
message,
|
message,
|
||||||
fallbackUserId: fallbackUserId ?? cpPeerUserId,
|
fallbackUserId: fallbackUserId ?? cpPeerUserId,
|
||||||
@ -816,7 +821,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _cpInviteConversationPeerIdFromData(Map<String, dynamic>? data) {
|
String? _cpInviteConversationPeerIdFromData(
|
||||||
|
Map<String, dynamic>? data, {
|
||||||
|
V2TimMessage? message,
|
||||||
|
}) {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -825,9 +833,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
if (invite == null || currentProfile == null) {
|
if (invite == null || currentProfile == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (_cpInviteSenderMatchesCurrent(invite, currentProfile)) {
|
final currentIds =
|
||||||
|
[currentProfile.id, currentProfile.account]
|
||||||
|
.map((value) => value?.trim() ?? "")
|
||||||
|
.where((value) => value.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
final sender = message?.sender?.trim() ?? "";
|
||||||
|
final userId = message?.userID?.trim() ?? "";
|
||||||
|
final isSender = _cpInviteSenderMatchesCurrent(
|
||||||
|
invite,
|
||||||
|
currentProfile,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
if (isSender) {
|
||||||
|
if (userId.isNotEmpty && !currentIds.contains(userId)) {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
return _cpInviteReceiverIdFromMessageData(data);
|
return _cpInviteReceiverIdFromMessageData(data);
|
||||||
}
|
}
|
||||||
|
if (sender.isNotEmpty && !currentIds.contains(sender)) {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
return _cpInviteInviterIdFromMessageData(data, invite);
|
return _cpInviteInviterIdFromMessageData(data, invite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,7 +934,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final isSender = _cpInviteSenderMatchesCurrent(invite, currentProfile);
|
final isSender = _cpInviteSenderMatchesCurrent(
|
||||||
|
invite,
|
||||||
|
currentProfile,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
final inviterName = _cpInviteDisplayName(
|
final inviterName = _cpInviteDisplayName(
|
||||||
nickname: invite.userNickname,
|
nickname: invite.userNickname,
|
||||||
account: invite.account,
|
account: invite.account,
|
||||||
@ -921,7 +951,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
final relationType = scNormalizeCpRelationType(invite.relationType);
|
final relationType = scNormalizeCpRelationType(invite.relationType);
|
||||||
if (isSender) {
|
if (isSender) {
|
||||||
final receiver = _cpInviteReceiverFromMessageData(data);
|
final receiver = _cpInviteReceiverFromMessageData(
|
||||||
|
data,
|
||||||
|
fallbackName: _cpInviteReceiverFallbackName(data, message),
|
||||||
|
fallbackAvatar: _cpInviteReceiverFallbackAvatar(data, message),
|
||||||
|
);
|
||||||
_cpInviteLog(
|
_cpInviteLog(
|
||||||
'show waiting CP_BUILD applyId=$applyId '
|
'show waiting CP_BUILD applyId=$applyId '
|
||||||
'relationType=$relationType',
|
'relationType=$relationType',
|
||||||
@ -952,6 +986,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
'relationType=$relationType '
|
'relationType=$relationType '
|
||||||
'inviter=$inviterName',
|
'inviter=$inviterName',
|
||||||
);
|
);
|
||||||
|
final currentReceiver = _cpInviteReceiverFromMessageData(
|
||||||
|
data,
|
||||||
|
fallbackName: _cpInviteReceiverFallbackName(data, null),
|
||||||
|
fallbackAvatar: _cpInviteReceiverFallbackAvatar(data, null),
|
||||||
|
);
|
||||||
await RoomCpInviteDialog.show(
|
await RoomCpInviteDialog.show(
|
||||||
context,
|
context,
|
||||||
inviter: RoomCpInviteDialogUser(
|
inviter: RoomCpInviteDialogUser(
|
||||||
@ -962,18 +1001,37 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
),
|
),
|
||||||
receiver: RoomCpInviteDialogUser(
|
receiver: RoomCpInviteDialogUser(
|
||||||
name: currentName,
|
name: currentName,
|
||||||
avatarUrl: currentProfile.userAvatar ?? "",
|
avatarUrl:
|
||||||
headdress: currentProfile.getHeaddress()?.sourceUrl ?? "",
|
_firstCpInviteValue([
|
||||||
headdressCover: currentProfile.getHeaddress()?.cover ?? "",
|
currentProfile.userAvatar,
|
||||||
|
currentReceiver.avatarUrl,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
headdress:
|
||||||
|
_firstCpInviteValue([
|
||||||
|
currentProfile.getHeaddress()?.sourceUrl,
|
||||||
|
currentReceiver.headdress,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
headdressCover:
|
||||||
|
_firstCpInviteValue([
|
||||||
|
currentProfile.getHeaddress()?.cover,
|
||||||
|
currentReceiver.headdressCover,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
style: RoomCpInviteDialogStyle.incoming,
|
style: RoomCpInviteDialogStyle.incoming,
|
||||||
countdownSeconds: _cpInviteCountdownSeconds(invite),
|
countdownSeconds: _cpInviteCountdownSeconds(invite),
|
||||||
descriptionText: _cpInviteDescriptionText(relationType),
|
descriptionText: _cpInviteDescriptionText(relationType),
|
||||||
onAccept: () {
|
onAccept: () {
|
||||||
unawaited(_processIncomingCpInvite(context, applyId, invite, true));
|
unawaited(
|
||||||
|
_processIncomingCpInvite(context, applyId, invite, true, data: data),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onReject: () {
|
onReject: () {
|
||||||
unawaited(_processIncomingCpInvite(context, applyId, invite, false));
|
unawaited(
|
||||||
|
_processIncomingCpInvite(context, applyId, invite, false, data: data),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onClose: () {
|
onClose: () {
|
||||||
_cpInviteLog('close incoming CP_BUILD applyId=$applyId');
|
_cpInviteLog('close incoming CP_BUILD applyId=$applyId');
|
||||||
@ -1066,6 +1124,29 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
if (context != null && invite != null && currentProfile != null) {
|
if (context != null && invite != null && currentProfile != null) {
|
||||||
|
final isSender = _cpInviteSenderMatchesCurrent(
|
||||||
|
invite,
|
||||||
|
currentProfile,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
if (result == _CpInviteProcessResult.rejected) {
|
||||||
|
if (isSender) {
|
||||||
|
_showCpInviteRejectedDialog(relationType, dedupKey: resultKey);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (result == _CpInviteProcessResult.accepted) {
|
||||||
|
if (isSender) {
|
||||||
|
_dispatchCpRelationEstablishedRoomNotice(
|
||||||
|
groupId: _currentVisibleVoiceRoomGroupId(context),
|
||||||
|
applyId: applyId,
|
||||||
|
data: data,
|
||||||
|
invite: invite,
|
||||||
|
currentProfile: currentProfile,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
unawaited(
|
unawaited(
|
||||||
_showCpInviteResultDialog(
|
_showCpInviteResultDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -1090,7 +1171,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
required _CpInviteProcessResult result,
|
required _CpInviteProcessResult result,
|
||||||
required String relationType,
|
required String relationType,
|
||||||
}) async {
|
}) async {
|
||||||
final isSender = _cpInviteSenderMatchesCurrent(invite, currentProfile);
|
final isSender = _cpInviteSenderMatchesCurrent(
|
||||||
|
invite,
|
||||||
|
currentProfile,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
final currentName = _cpInviteDisplayName(
|
final currentName = _cpInviteDisplayName(
|
||||||
nickname: currentProfile.userNickname,
|
nickname: currentProfile.userNickname,
|
||||||
account: currentProfile.account,
|
account: currentProfile.account,
|
||||||
@ -1105,6 +1190,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
'show CP invite result dialog result=${result.name} '
|
'show CP invite result dialog result=${result.name} '
|
||||||
'relationType=$relationType isSender=$isSender',
|
'relationType=$relationType isSender=$isSender',
|
||||||
);
|
);
|
||||||
|
final currentReceiver = _cpInviteReceiverFromMessageData(data);
|
||||||
await RoomCpInviteDialog.show(
|
await RoomCpInviteDialog.show(
|
||||||
context,
|
context,
|
||||||
inviter: RoomCpInviteDialogUser(
|
inviter: RoomCpInviteDialogUser(
|
||||||
@ -1124,12 +1210,31 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
),
|
),
|
||||||
receiver:
|
receiver:
|
||||||
isSender
|
isSender
|
||||||
? _cpInviteReceiverFromMessageData(data)
|
? _cpInviteReceiverFromMessageData(
|
||||||
|
data,
|
||||||
|
fallbackName: _cpInviteReceiverFallbackName(data, null),
|
||||||
|
fallbackAvatar: _cpInviteReceiverFallbackAvatar(data, null),
|
||||||
|
)
|
||||||
: RoomCpInviteDialogUser(
|
: RoomCpInviteDialogUser(
|
||||||
name: currentName,
|
name: currentName,
|
||||||
avatarUrl: currentProfile.userAvatar ?? "",
|
avatarUrl:
|
||||||
headdress: currentProfile.getHeaddress()?.sourceUrl ?? "",
|
_firstCpInviteValue([
|
||||||
headdressCover: currentProfile.getHeaddress()?.cover ?? "",
|
currentProfile.userAvatar,
|
||||||
|
currentReceiver.avatarUrl,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
headdress:
|
||||||
|
_firstCpInviteValue([
|
||||||
|
currentProfile.getHeaddress()?.sourceUrl,
|
||||||
|
currentReceiver.headdress,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
headdressCover:
|
||||||
|
_firstCpInviteValue([
|
||||||
|
currentProfile.getHeaddress()?.cover,
|
||||||
|
currentReceiver.headdressCover,
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
),
|
),
|
||||||
style:
|
style:
|
||||||
isSender
|
isSender
|
||||||
@ -1271,13 +1376,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
String applyId,
|
String applyId,
|
||||||
SCSystemInvitMessageRes invite,
|
SCSystemInvitMessageRes invite,
|
||||||
bool agree,
|
bool agree, {
|
||||||
) async {
|
Map<String, dynamic>? data,
|
||||||
|
}) async {
|
||||||
_cpInviteLog('process CP_BUILD applyId=$applyId agree=$agree');
|
_cpInviteLog('process CP_BUILD applyId=$applyId agree=$agree');
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
|
final voiceRoomGroupId = _currentVisibleVoiceRoomGroupId(context);
|
||||||
final success = await profileManager.cpRlationshipProcessApply(
|
final success = await profileManager.cpRlationshipProcessApply(
|
||||||
context,
|
context,
|
||||||
applyId,
|
applyId,
|
||||||
@ -1295,6 +1402,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
|
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
|
||||||
unawaited(profileManager.refreshLoadedCpProfiles());
|
unawaited(profileManager.refreshLoadedCpProfiles());
|
||||||
|
if (agree) {
|
||||||
|
_dispatchCpRelationEstablishedRoomNotice(
|
||||||
|
groupId: voiceRoomGroupId,
|
||||||
|
applyId: applyId,
|
||||||
|
data: data,
|
||||||
|
invite: invite,
|
||||||
|
currentProfile: AccountStorage().getCurrentUser()?.userProfile,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_dispatchCpInviteRejectedRoomNotice(
|
||||||
|
groupId: voiceRoomGroupId,
|
||||||
|
applyId: applyId,
|
||||||
|
data: data,
|
||||||
|
invite: invite,
|
||||||
|
currentProfile: AccountStorage().getCurrentUser()?.userProfile,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendCpRelationEstablishedSystemMessage({
|
Future<void> sendCpRelationEstablishedSystemMessage({
|
||||||
@ -1308,6 +1432,183 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _dispatchCpRelationEstablishedRoomNotice({
|
||||||
|
required String groupId,
|
||||||
|
required String applyId,
|
||||||
|
required SCSystemInvitMessageRes invite,
|
||||||
|
required SocialChatUserProfile? currentProfile,
|
||||||
|
Map<String, dynamic>? data,
|
||||||
|
}) {
|
||||||
|
if (currentProfile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (groupId.isEmpty) {
|
||||||
|
_cpInviteLog(
|
||||||
|
'skip room established notice reason=no_visible_voice_room '
|
||||||
|
'applyId=$applyId',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final isSender = _cpInviteSenderMatchesCurrent(
|
||||||
|
invite,
|
||||||
|
currentProfile,
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
final inviter = isSender ? currentProfile : _cpInviteInviterProfile(invite);
|
||||||
|
final receiver = isSender ? _cpInviteReceiverProfile(data) : currentProfile;
|
||||||
|
final relationType = scNormalizeCpRelationType(
|
||||||
|
_firstCpInviteValue([
|
||||||
|
invite.relationType,
|
||||||
|
data == null ? null : data["relationType"]?.toString(),
|
||||||
|
data == null ? null : data["relation_type"]?.toString(),
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
final targetUserIds = _compactCpNoticeTargetIds([inviter, receiver]);
|
||||||
|
_cpInviteLog(
|
||||||
|
'dispatch room established notice applyId=$applyId '
|
||||||
|
'relationType=$relationType groupId=$groupId '
|
||||||
|
'isSender=$isSender targets=${targetUserIds.join(",")}',
|
||||||
|
);
|
||||||
|
unawaited(
|
||||||
|
dispatchMessage(
|
||||||
|
Msg(
|
||||||
|
groupId: groupId,
|
||||||
|
msg: scBuildCpRelationEstablishedNotice(
|
||||||
|
userName1: _roomMsgUserDisplayName(inviter),
|
||||||
|
userName2: _roomMsgUserDisplayName(receiver),
|
||||||
|
relationType: relationType,
|
||||||
|
),
|
||||||
|
type: SCRoomMsgType.cpSystemNotice,
|
||||||
|
noticeAction: "RELATION_ESTABLISHED",
|
||||||
|
user: receiver,
|
||||||
|
toUser: inviter,
|
||||||
|
targetUserIds: targetUserIds,
|
||||||
|
giftBatchId: applyId,
|
||||||
|
role: relationType,
|
||||||
|
),
|
||||||
|
addLocal: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _dispatchCpInviteRejectedRoomNotice({
|
||||||
|
required String groupId,
|
||||||
|
required String applyId,
|
||||||
|
required SCSystemInvitMessageRes invite,
|
||||||
|
required SocialChatUserProfile? currentProfile,
|
||||||
|
Map<String, dynamic>? data,
|
||||||
|
}) {
|
||||||
|
if (currentProfile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (groupId.isEmpty) {
|
||||||
|
_cpInviteLog(
|
||||||
|
'skip room rejected notice reason=no_visible_voice_room '
|
||||||
|
'applyId=$applyId',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final inviter = _cpInviteInviterProfile(invite);
|
||||||
|
final targetUserIds = _compactCpNoticeTargetIds([inviter]);
|
||||||
|
final relationType = scNormalizeCpRelationType(
|
||||||
|
_firstCpInviteValue([
|
||||||
|
invite.relationType,
|
||||||
|
data == null ? null : data["relationType"]?.toString(),
|
||||||
|
data == null ? null : data["relation_type"]?.toString(),
|
||||||
|
]) ??
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
_cpInviteLog(
|
||||||
|
'dispatch room rejected notice applyId=$applyId '
|
||||||
|
'relationType=$relationType groupId=$groupId '
|
||||||
|
'targets=${targetUserIds.join(",")}',
|
||||||
|
);
|
||||||
|
unawaited(
|
||||||
|
dispatchMessage(
|
||||||
|
Msg(
|
||||||
|
groupId: groupId,
|
||||||
|
msg: "",
|
||||||
|
type: SCRoomMsgType.cpSystemNotice,
|
||||||
|
noticeAction: "RELATION_REJECTED",
|
||||||
|
user: currentProfile,
|
||||||
|
toUser: inviter,
|
||||||
|
targetUserIds: targetUserIds,
|
||||||
|
giftBatchId: applyId,
|
||||||
|
role: relationType,
|
||||||
|
),
|
||||||
|
addLocal: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _currentVisibleVoiceRoomGroupId(BuildContext context) {
|
||||||
|
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
|
if (!rtcProvider.isVoiceRoomRouteVisible) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return (rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile _cpInviteInviterProfile(
|
||||||
|
SCSystemInvitMessageRes invite,
|
||||||
|
) {
|
||||||
|
final userId =
|
||||||
|
_firstCpInviteValue([invite.actualAccount, invite.account]) ?? "";
|
||||||
|
return SocialChatUserProfile(
|
||||||
|
id: userId,
|
||||||
|
account:
|
||||||
|
_firstCpInviteValue([invite.account, invite.actualAccount]) ?? userId,
|
||||||
|
userNickname: _cpInviteDisplayName(
|
||||||
|
nickname: invite.userNickname,
|
||||||
|
account: invite.account,
|
||||||
|
actualAccount: invite.actualAccount,
|
||||||
|
fallback: userId,
|
||||||
|
),
|
||||||
|
userAvatar: invite.userAvatar ?? "",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile _cpInviteReceiverProfile(Map<String, dynamic>? data) {
|
||||||
|
final receiverData =
|
||||||
|
data == null
|
||||||
|
? const <String, dynamic>{}
|
||||||
|
: Map<String, dynamic>.from(data);
|
||||||
|
final receiver = _cpInviteReceiverFromMessageData(receiverData);
|
||||||
|
final userId = _cpInviteReceiverIdFromMessageData(receiverData);
|
||||||
|
return SocialChatUserProfile(
|
||||||
|
id: userId,
|
||||||
|
account:
|
||||||
|
_firstCpInviteValue([
|
||||||
|
receiverData["acceptAccount"]?.toString(),
|
||||||
|
receiverData["receiverAccount"]?.toString(),
|
||||||
|
userId,
|
||||||
|
]) ??
|
||||||
|
userId,
|
||||||
|
userNickname: receiver.name,
|
||||||
|
userAvatar: receiver.avatarUrl,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _compactCpNoticeTargetIds(
|
||||||
|
Iterable<SocialChatUserProfile?> profiles,
|
||||||
|
) {
|
||||||
|
final result = <String>[];
|
||||||
|
for (final profile in profiles) {
|
||||||
|
for (final value in [profile?.id, profile?.account]) {
|
||||||
|
final text = value?.trim() ?? "";
|
||||||
|
if (text.isNotEmpty && !result.contains(text)) {
|
||||||
|
result.add(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> sendCpInviteRejectedSystemMessage({
|
Future<void> sendCpInviteRejectedSystemMessage({
|
||||||
required String applyId,
|
required String applyId,
|
||||||
required SCSystemInvitMessageRes invite,
|
required SCSystemInvitMessageRes invite,
|
||||||
@ -1334,8 +1635,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
data["senderUserId"]?.toString(),
|
data["senderUserId"]?.toString(),
|
||||||
data["senderId"]?.toString(),
|
data["senderId"]?.toString(),
|
||||||
data["fromUserId"]?.toString(),
|
data["fromUserId"]?.toString(),
|
||||||
data["userId"]?.toString(),
|
data["applyUserId"]?.toString(),
|
||||||
content["actualAccount"]?.toString(),
|
data["account"]?.toString(),
|
||||||
content["applyActualAccount"]?.toString(),
|
content["applyActualAccount"]?.toString(),
|
||||||
content["applyUserActualAccount"]?.toString(),
|
content["applyUserActualAccount"]?.toString(),
|
||||||
content["inviterActualAccount"]?.toString(),
|
content["inviterActualAccount"]?.toString(),
|
||||||
@ -1344,11 +1645,14 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
content["senderUserId"]?.toString(),
|
content["senderUserId"]?.toString(),
|
||||||
content["senderId"]?.toString(),
|
content["senderId"]?.toString(),
|
||||||
content["fromUserId"]?.toString(),
|
content["fromUserId"]?.toString(),
|
||||||
content["userId"]?.toString(),
|
|
||||||
invite.actualAccount,
|
|
||||||
data["applyUserId"]?.toString(),
|
|
||||||
content["applyUserId"]?.toString(),
|
content["applyUserId"]?.toString(),
|
||||||
|
content["applyUserAccount"]?.toString(),
|
||||||
|
invite.actualAccount,
|
||||||
invite.account,
|
invite.account,
|
||||||
|
data["userId"]?.toString(),
|
||||||
|
content["actualAccount"]?.toString(),
|
||||||
|
content["userId"]?.toString(),
|
||||||
|
content["account"]?.toString(),
|
||||||
]) ??
|
]) ??
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
@ -1429,13 +1733,24 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
|
|
||||||
bool _cpInviteSenderMatchesCurrent(
|
bool _cpInviteSenderMatchesCurrent(
|
||||||
SCSystemInvitMessageRes invite,
|
SCSystemInvitMessageRes invite,
|
||||||
SocialChatUserProfile currentProfile,
|
SocialChatUserProfile currentProfile, {
|
||||||
) {
|
Map<String, dynamic>? data,
|
||||||
|
}) {
|
||||||
final currentIds =
|
final currentIds =
|
||||||
[currentProfile.id, currentProfile.account]
|
[currentProfile.id, currentProfile.account]
|
||||||
.map((value) => value?.trim() ?? "")
|
.map((value) => value?.trim() ?? "")
|
||||||
.where((value) => value.isNotEmpty)
|
.where((value) => value.isNotEmpty)
|
||||||
.toSet();
|
.toSet();
|
||||||
|
if (data != null) {
|
||||||
|
final receiverIds = _cpInviteReceiverIdentityValues(data);
|
||||||
|
if (currentIds.any(receiverIds.contains)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final explicitInviteIds = _cpInviteInviterIdentityValues(data);
|
||||||
|
if (explicitInviteIds.isNotEmpty) {
|
||||||
|
return currentIds.any(explicitInviteIds.contains);
|
||||||
|
}
|
||||||
|
}
|
||||||
final inviteIds =
|
final inviteIds =
|
||||||
[invite.actualAccount, invite.account]
|
[invite.actualAccount, invite.account]
|
||||||
.map((value) => value?.trim() ?? "")
|
.map((value) => value?.trim() ?? "")
|
||||||
@ -1444,9 +1759,68 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return currentIds.any(inviteIds.contains);
|
return currentIds.any(inviteIds.contains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
|
||||||
|
final content = _cpInviteContentMap(data);
|
||||||
|
final values = <String?>[
|
||||||
|
data["account"]?.toString(),
|
||||||
|
data["actualAccount"]?.toString(),
|
||||||
|
data["applyUserId"]?.toString(),
|
||||||
|
data["applyUserAccount"]?.toString(),
|
||||||
|
data["applyActualAccount"]?.toString(),
|
||||||
|
data["applyUserActualAccount"]?.toString(),
|
||||||
|
data["inviterActualAccount"]?.toString(),
|
||||||
|
data["inviterUserId"]?.toString(),
|
||||||
|
data["inviterId"]?.toString(),
|
||||||
|
data["senderUserId"]?.toString(),
|
||||||
|
data["senderId"]?.toString(),
|
||||||
|
data["fromUserId"]?.toString(),
|
||||||
|
content["applyUserId"]?.toString(),
|
||||||
|
content["applyUserAccount"]?.toString(),
|
||||||
|
content["applyActualAccount"]?.toString(),
|
||||||
|
content["applyUserActualAccount"]?.toString(),
|
||||||
|
content["inviterActualAccount"]?.toString(),
|
||||||
|
content["inviterUserId"]?.toString(),
|
||||||
|
content["inviterId"]?.toString(),
|
||||||
|
content["senderUserId"]?.toString(),
|
||||||
|
content["senderId"]?.toString(),
|
||||||
|
content["fromUserId"]?.toString(),
|
||||||
|
];
|
||||||
|
return values.map((value) => value?.trim() ?? "").where((value) {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpInviteReceiverIdentityValues(Map<String, dynamic> data) {
|
||||||
|
final content = _cpInviteContentMap(data);
|
||||||
|
return [
|
||||||
|
data["acceptUserId"]?.toString(),
|
||||||
|
data["acceptAccount"]?.toString(),
|
||||||
|
data["acceptActualAccount"]?.toString(),
|
||||||
|
data["receiverId"]?.toString(),
|
||||||
|
data["receiverUserId"]?.toString(),
|
||||||
|
data["receiverAccount"]?.toString(),
|
||||||
|
data["receiverActualAccount"]?.toString(),
|
||||||
|
data["targetUserId"]?.toString(),
|
||||||
|
data["toUserId"]?.toString(),
|
||||||
|
content["acceptUserId"]?.toString(),
|
||||||
|
content["acceptAccount"]?.toString(),
|
||||||
|
content["acceptActualAccount"]?.toString(),
|
||||||
|
content["receiverId"]?.toString(),
|
||||||
|
content["receiverUserId"]?.toString(),
|
||||||
|
content["receiverAccount"]?.toString(),
|
||||||
|
content["receiverActualAccount"]?.toString(),
|
||||||
|
content["targetUserId"]?.toString(),
|
||||||
|
content["toUserId"]?.toString(),
|
||||||
|
].map((value) => value?.trim() ?? "").where((value) {
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
RoomCpInviteDialogUser _cpInviteReceiverFromMessageData(
|
RoomCpInviteDialogUser _cpInviteReceiverFromMessageData(
|
||||||
Map<String, dynamic> data,
|
Map<String, dynamic> data, {
|
||||||
) {
|
String? fallbackName,
|
||||||
|
String? fallbackAvatar,
|
||||||
|
}) {
|
||||||
final content = _cpInviteContentMap(data);
|
final content = _cpInviteContentMap(data);
|
||||||
final name = _cpInviteDisplayName(
|
final name = _cpInviteDisplayName(
|
||||||
nickname: _firstCpInviteValue([
|
nickname: _firstCpInviteValue([
|
||||||
@ -1472,6 +1846,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
content["receiverActualAccount"]?.toString(),
|
content["receiverActualAccount"]?.toString(),
|
||||||
]),
|
]),
|
||||||
fallback: _firstCpInviteValue([
|
fallback: _firstCpInviteValue([
|
||||||
|
fallbackName,
|
||||||
data["acceptUserId"]?.toString(),
|
data["acceptUserId"]?.toString(),
|
||||||
data["receiverId"]?.toString(),
|
data["receiverId"]?.toString(),
|
||||||
data["receiverUserId"]?.toString(),
|
data["receiverUserId"]?.toString(),
|
||||||
@ -1490,6 +1865,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
content["acceptUserAvatar"]?.toString(),
|
content["acceptUserAvatar"]?.toString(),
|
||||||
content["receiverAvatar"]?.toString(),
|
content["receiverAvatar"]?.toString(),
|
||||||
content["receiverUserAvatar"]?.toString(),
|
content["receiverUserAvatar"]?.toString(),
|
||||||
|
fallbackAvatar,
|
||||||
]) ??
|
]) ??
|
||||||
"",
|
"",
|
||||||
headdress:
|
headdress:
|
||||||
@ -1523,6 +1899,33 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? _cpInviteReceiverFallbackName(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
V2TimMessage? message,
|
||||||
|
) {
|
||||||
|
final receiverId = _cpInviteReceiverIdFromMessageData(data);
|
||||||
|
final conversation =
|
||||||
|
receiverId.isNotEmpty ? conversationMap["c2c_$receiverId"] : null;
|
||||||
|
return _firstCpInviteValue([
|
||||||
|
conversation?.showName,
|
||||||
|
message?.friendRemark,
|
||||||
|
message?.nickName,
|
||||||
|
message?.nameCard,
|
||||||
|
receiverId,
|
||||||
|
message?.userID,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _cpInviteReceiverFallbackAvatar(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
V2TimMessage? message,
|
||||||
|
) {
|
||||||
|
final receiverId = _cpInviteReceiverIdFromMessageData(data);
|
||||||
|
final conversation =
|
||||||
|
receiverId.isNotEmpty ? conversationMap["c2c_$receiverId"] : null;
|
||||||
|
return _firstCpInviteValue([conversation?.faceUrl, message?.faceUrl]);
|
||||||
|
}
|
||||||
|
|
||||||
String? _firstCpInviteValue(Iterable<String?> values) {
|
String? _firstCpInviteValue(Iterable<String?> values) {
|
||||||
for (final value in values) {
|
for (final value in values) {
|
||||||
final text = value?.trim() ?? "";
|
final text = value?.trim() ?? "";
|
||||||
@ -2065,9 +2468,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
if (msg.type == SCRoomMsgType.cpSystemNotice &&
|
if (msg.type == SCRoomMsgType.cpSystemNotice &&
|
||||||
msg.noticeAction == "RELATION_REJECTED") {
|
msg.noticeAction == "RELATION_REJECTED") {
|
||||||
|
if (_isDuplicateCpRelationNotice(msg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_showCpInviteRejectedDialogIfNeeded(msg);
|
_showCpInviteRejectedDialogIfNeeded(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (msg.type == SCRoomMsgType.cpSystemNotice &&
|
||||||
|
_isDuplicateCpRelationNotice(msg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
roomAllMsgList.insert(0, msg);
|
roomAllMsgList.insert(0, msg);
|
||||||
if (roomAllMsgList.length > 250) {
|
if (roomAllMsgList.length > 250) {
|
||||||
@ -2168,6 +2578,69 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isDuplicateCpRelationNotice(Msg msg) {
|
||||||
|
if (msg.type != SCRoomMsgType.cpSystemNotice) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final action = msg.noticeAction?.trim() ?? "";
|
||||||
|
if (action != "RELATION_ESTABLISHED" &&
|
||||||
|
action != "RELATION_JOINED_ROOM" &&
|
||||||
|
action != "RELATION_REJECTED") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final key = _cpRelationNoticeDedupKey(msg);
|
||||||
|
if (key.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
_recentCpRelationNoticeTimes.removeWhere((_, timestamp) {
|
||||||
|
return now - timestamp > 60000;
|
||||||
|
});
|
||||||
|
final lastShownAt = _recentCpRelationNoticeTimes[key];
|
||||||
|
if (lastShownAt != null && now - lastShownAt < 60000) {
|
||||||
|
_cpInviteLog('skip duplicated room CP notice key=$key');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_recentCpRelationNoticeTimes[key] = now;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cpRelationNoticeDedupKey(Msg msg) {
|
||||||
|
final explicitKey = msg.giftBatchId?.trim() ?? "";
|
||||||
|
final action = msg.noticeAction?.trim() ?? "";
|
||||||
|
final relationType = scNormalizeCpRelationType(msg.role);
|
||||||
|
final ids =
|
||||||
|
[_roomMsgUserId(msg.user), _roomMsgUserId(msg.toUser)]
|
||||||
|
..removeWhere((value) => value.isEmpty)
|
||||||
|
..sort();
|
||||||
|
if (ids.isNotEmpty) {
|
||||||
|
return [
|
||||||
|
"cp_notice",
|
||||||
|
action,
|
||||||
|
msg.groupId ?? "",
|
||||||
|
relationType,
|
||||||
|
...ids,
|
||||||
|
].join("|");
|
||||||
|
}
|
||||||
|
if (explicitKey.isNotEmpty) {
|
||||||
|
return "cp_notice:$action:$explicitKey";
|
||||||
|
}
|
||||||
|
if (ids.isEmpty) {
|
||||||
|
final names = [
|
||||||
|
_roomMsgUserDisplayName(msg.user),
|
||||||
|
_roomMsgUserDisplayName(msg.toUser),
|
||||||
|
]..sort();
|
||||||
|
ids.addAll(names);
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
"cp_notice",
|
||||||
|
action,
|
||||||
|
msg.groupId ?? "",
|
||||||
|
relationType,
|
||||||
|
...ids,
|
||||||
|
].join("|");
|
||||||
|
}
|
||||||
|
|
||||||
bool _isLiveCpRelationBroadcastMessage(Msg msg) {
|
bool _isLiveCpRelationBroadcastMessage(Msg msg) {
|
||||||
final timestamp = msg.time ?? 0;
|
final timestamp = msg.time ?? 0;
|
||||||
if (timestamp <= 0) {
|
if (timestamp <= 0) {
|
||||||
@ -2203,8 +2676,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
if (currentProfile == null ||
|
if (currentProfile == null) {
|
||||||
_isSameRoomMsgUser(currentProfile, msg.user)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final targetIds =
|
final targetIds =
|
||||||
@ -2214,6 +2686,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
.toSet() ??
|
.toSet() ??
|
||||||
const <String>{};
|
const <String>{};
|
||||||
final isTarget =
|
final isTarget =
|
||||||
|
_isSameRoomMsgUser(currentProfile, msg.user) ||
|
||||||
_isSameRoomMsgUser(currentProfile, msg.toUser) ||
|
_isSameRoomMsgUser(currentProfile, msg.toUser) ||
|
||||||
targetIds.contains(currentProfile.id?.trim() ?? "") ||
|
targetIds.contains(currentProfile.id?.trim() ?? "") ||
|
||||||
targetIds.contains(currentProfile.account?.trim() ?? "");
|
targetIds.contains(currentProfile.account?.trim() ?? "");
|
||||||
@ -2250,10 +2723,26 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
|
SmartDialog.dismiss(tag: RoomCpInviteDialog.dialogTag);
|
||||||
_showCpInviteRejectedDialog(msg.role ?? "");
|
_showCpInviteRejectedDialog(
|
||||||
|
msg.role ?? "",
|
||||||
|
dedupKey: _cpRelationRejectedDialogDedupKey(msg),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showCpInviteRejectedDialog(String relationType) {
|
String _cpRelationRejectedDialogDedupKey(Msg msg) {
|
||||||
|
final explicitKey = msg.giftBatchId?.trim() ?? "";
|
||||||
|
if (explicitKey.isNotEmpty) {
|
||||||
|
return "rejected_$explicitKey";
|
||||||
|
}
|
||||||
|
return _cpRelationNoticeDedupKey(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showCpInviteRejectedDialog(String relationType, {String? dedupKey}) {
|
||||||
|
final key = dedupKey?.trim() ?? "";
|
||||||
|
if (key.isNotEmpty && !_shownCpInviteRejectedDialogKeys.add(key)) {
|
||||||
|
_cpInviteLog('skip duplicated rejected dialog key=$key');
|
||||||
|
return;
|
||||||
|
}
|
||||||
final context = navigatorKey.currentState?.context;
|
final context = navigatorKey.currentState?.context;
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
_cpInviteLog(
|
_cpInviteLog(
|
||||||
|
|||||||
@ -166,8 +166,8 @@ class SCSystemInvitMessageRes {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final source = <String, dynamic>{
|
final source = <String, dynamic>{
|
||||||
...root,
|
|
||||||
if (content.isNotEmpty) ...content,
|
if (content.isNotEmpty) ...content,
|
||||||
|
...root,
|
||||||
};
|
};
|
||||||
final relationType =
|
final relationType =
|
||||||
_firstNonBlank(source, _relationTypeKeys) ??
|
_firstNonBlank(source, _relationTypeKeys) ??
|
||||||
|
|||||||
@ -736,6 +736,7 @@ class OverlayManager {
|
|||||||
_removeMessagesByType(1);
|
_removeMessagesByType(1);
|
||||||
_removeMessagesByType(0);
|
_removeMessagesByType(0);
|
||||||
_removeMessagesByType(5);
|
_removeMessagesByType(5);
|
||||||
|
_removeMessagesByType(6);
|
||||||
if (normalizedRoomId.isNotEmpty) {
|
if (normalizedRoomId.isNotEmpty) {
|
||||||
_removeMessagesByType(3, roomId: normalizedRoomId);
|
_removeMessagesByType(3, roomId: normalizedRoomId);
|
||||||
}
|
}
|
||||||
@ -871,6 +872,9 @@ class OverlayManager {
|
|||||||
if (message.type == 4) {
|
if (message.type == 4) {
|
||||||
return _isCurrentRoomFloatingMessage(context, message);
|
return _isCurrentRoomFloatingMessage(context, message);
|
||||||
}
|
}
|
||||||
|
if (message.type == 6) {
|
||||||
|
return _isCurrentRoomFloatingMessage(context, message);
|
||||||
|
}
|
||||||
if (SCGlobalConfig.isFloatingBroadcastRoomOnly) {
|
if (SCGlobalConfig.isFloatingBroadcastRoomOnly) {
|
||||||
return _isCurrentRoomFloatingMessage(context, message);
|
return _isCurrentRoomFloatingMessage(context, message);
|
||||||
}
|
}
|
||||||
@ -895,11 +899,15 @@ class OverlayManager {
|
|||||||
}
|
}
|
||||||
final currentRoomId =
|
final currentRoomId =
|
||||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? "";
|
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? "";
|
||||||
|
final currentRoomGroupId =
|
||||||
|
rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount?.trim() ??
|
||||||
|
"";
|
||||||
final messageRoomId = (message.roomId ?? "").trim();
|
final messageRoomId = (message.roomId ?? "").trim();
|
||||||
if (currentRoomId.isEmpty || messageRoomId.isEmpty) {
|
if (currentRoomId.isEmpty || messageRoomId.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return currentRoomId == messageRoomId;
|
return currentRoomId == messageRoomId ||
|
||||||
|
(currentRoomGroupId.isNotEmpty && currentRoomGroupId == messageRoomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldDisplayRegionBroadcastMessage(
|
bool _shouldDisplayRegionBroadcastMessage(
|
||||||
|
|||||||
@ -346,7 +346,7 @@ class _MessageConversationListPageState
|
|||||||
}
|
}
|
||||||
_loadingConversationUserIds.add(userId);
|
_loadingConversationUserIds.add(userId);
|
||||||
SCAccountRepository()
|
SCAccountRepository()
|
||||||
.loadUserInfo(userId)
|
.loadUserInfo(userId, silentErrorToast: true)
|
||||||
.then((value) {
|
.then((value) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -411,7 +411,14 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
_effectiveUserId.isNotEmpty;
|
_effectiveUserId.isNotEmpty;
|
||||||
|
|
||||||
String get _effectiveUserId {
|
String get _effectiveUserId {
|
||||||
final cpInvitePeerId = _cpInvitePeerUserIdFromLastMessage();
|
final cpInviteProfile = _cpInvitePeerProfileFromLastMessage();
|
||||||
|
final cpInvitePeerId =
|
||||||
|
_firstCpInviteValue([
|
||||||
|
_cpInvitePeerUserIdFromLastMessage(),
|
||||||
|
cpInviteProfile?.id,
|
||||||
|
cpInviteProfile?.account,
|
||||||
|
]) ??
|
||||||
|
"";
|
||||||
if (cpInvitePeerId.isNotEmpty) {
|
if (cpInvitePeerId.isNotEmpty) {
|
||||||
return cpInvitePeerId;
|
return cpInvitePeerId;
|
||||||
}
|
}
|
||||||
@ -423,6 +430,11 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
if (userAvatar.isNotEmpty) {
|
if (userAvatar.isNotEmpty) {
|
||||||
return userAvatar;
|
return userAvatar;
|
||||||
}
|
}
|
||||||
|
final cpInviteAvatar =
|
||||||
|
_cpInvitePeerProfileFromLastMessage()?.userAvatar ?? "";
|
||||||
|
if (cpInviteAvatar.isNotEmpty) {
|
||||||
|
return cpInviteAvatar;
|
||||||
|
}
|
||||||
return conversation.faceUrl ?? "";
|
return conversation.faceUrl ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,6 +443,17 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
if (nickname.isNotEmpty) {
|
if (nickname.isNotEmpty) {
|
||||||
return nickname;
|
return nickname;
|
||||||
}
|
}
|
||||||
|
final cpInviteProfile = _cpInvitePeerProfileFromLastMessage();
|
||||||
|
final cpInviteName =
|
||||||
|
_firstCpInviteValue([
|
||||||
|
cpInviteProfile?.userNickname,
|
||||||
|
cpInviteProfile?.id,
|
||||||
|
cpInviteProfile?.account,
|
||||||
|
]) ??
|
||||||
|
"";
|
||||||
|
if (cpInviteName.isNotEmpty) {
|
||||||
|
return cpInviteName;
|
||||||
|
}
|
||||||
return conversation.showName ??
|
return conversation.showName ??
|
||||||
conversation.userID ??
|
conversation.userID ??
|
||||||
conversation.groupID ??
|
conversation.groupID ??
|
||||||
@ -439,7 +462,7 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
user = widget.initialUser;
|
user = widget.initialUser ?? _cpInvitePeerProfileFromLastMessage();
|
||||||
loadUserInfo();
|
loadUserInfo();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@ -449,6 +472,11 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (widget.initialUser != null && widget.initialUser != user) {
|
if (widget.initialUser != null && widget.initialUser != user) {
|
||||||
user = widget.initialUser;
|
user = widget.initialUser;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final cpInviteProfile = _cpInvitePeerProfileFromLastMessage();
|
||||||
|
if (user == null && cpInviteProfile != null) {
|
||||||
|
user = cpInviteProfile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,26 +700,50 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void loadUserInfo() {
|
void loadUserInfo() {
|
||||||
if (!_shouldLoadUserInfo || user != null) return;
|
if (!_shouldLoadUserInfo) return;
|
||||||
final userId = _effectiveUserId;
|
final userId = _effectiveUserId;
|
||||||
if (userId.isEmpty) return;
|
if (userId.isEmpty) return;
|
||||||
SCAccountRepository().loadUserInfo(userId).then((value) {
|
SCAccountRepository()
|
||||||
if (!mounted) return;
|
.loadUserInfo(userId, silentErrorToast: true)
|
||||||
user = value;
|
.then((value) {
|
||||||
setState(() {});
|
if (!mounted) return;
|
||||||
});
|
user = value;
|
||||||
|
setState(() {});
|
||||||
|
})
|
||||||
|
.catchError((_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
user ??= _cpInvitePeerProfileFromLastMessage();
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
V2TimConversation _routeConversation() {
|
V2TimConversation _routeConversation() {
|
||||||
final peerId = _effectiveUserId;
|
final peerId = _effectiveUserId;
|
||||||
if (!_isC2CConversation ||
|
if (!_isC2CConversation || peerId.isEmpty) {
|
||||||
peerId.isEmpty ||
|
|
||||||
peerId == (conversation.userID?.trim() ?? "")) {
|
|
||||||
return conversation;
|
return conversation;
|
||||||
}
|
}
|
||||||
final json = conversation.toJson();
|
final json = conversation.toJson();
|
||||||
json["userID"] = peerId;
|
final isSamePeer = peerId == (conversation.userID?.trim() ?? "");
|
||||||
json["conversationID"] = "c2c_$peerId";
|
if (!isSamePeer) {
|
||||||
|
json["userID"] = peerId;
|
||||||
|
json["conversationID"] = "c2c_$peerId";
|
||||||
|
}
|
||||||
|
final profile = _cpInvitePeerProfileFromLastMessage();
|
||||||
|
final profileName = _firstCpInviteValue([
|
||||||
|
profile?.userNickname,
|
||||||
|
profile?.id,
|
||||||
|
profile?.account,
|
||||||
|
]);
|
||||||
|
if ((profileName ?? "").isNotEmpty) {
|
||||||
|
json["showName"] = profileName;
|
||||||
|
}
|
||||||
|
final profileAvatar = profile?.userAvatar?.trim() ?? "";
|
||||||
|
if (profileAvatar.isNotEmpty) {
|
||||||
|
json["faceUrl"] = profileAvatar;
|
||||||
|
}
|
||||||
|
if (isSamePeer && profileName == null && profileAvatar.isEmpty) {
|
||||||
|
return conversation;
|
||||||
|
}
|
||||||
return V2TimConversation.fromJson(json);
|
return V2TimConversation.fromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,10 +751,73 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
if (!_isC2CConversation) {
|
if (!_isC2CConversation) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
final message = conversation.lastMessage;
|
||||||
final data = _cpInviteCustomData(conversation.lastMessage);
|
final data = _cpInviteCustomData(conversation.lastMessage);
|
||||||
if (data == null) {
|
if (data == null || !_isCpBuildInviteData(data)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
|
final currentIds =
|
||||||
|
<String?>[currentProfile?.id, currentProfile?.account]
|
||||||
|
.map((value) => value?.trim() ?? "")
|
||||||
|
.where((value) => value.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
final isCurrentInviter = _cpInviteCurrentUserIsInviter(data);
|
||||||
|
final peerProfile = _cpInvitePeerProfileFromData(data);
|
||||||
|
final sender = message?.sender?.trim() ?? "";
|
||||||
|
final messageUserId =
|
||||||
|
_firstCpInviteValue([message?.userID, conversation.userID]) ?? "";
|
||||||
|
if (isCurrentInviter) {
|
||||||
|
return _firstCpInviteValue([
|
||||||
|
peerProfile?.id,
|
||||||
|
currentIds.contains(messageUserId) ? null : messageUserId,
|
||||||
|
_cpInviteReceiverIdFromData(data),
|
||||||
|
peerProfile?.account,
|
||||||
|
]) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
return _firstCpInviteValue([
|
||||||
|
currentIds.contains(sender) ? null : sender,
|
||||||
|
peerProfile?.id,
|
||||||
|
_cpInviteInviterIdFromData(data),
|
||||||
|
peerProfile?.account,
|
||||||
|
]) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile? _cpInvitePeerProfileFromLastMessage() {
|
||||||
|
final data = _cpInviteCustomData(conversation.lastMessage);
|
||||||
|
if (data == null || !_isCpBuildInviteData(data)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _cpInvitePeerProfileFromData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile? _cpInvitePeerProfileFromData(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
) {
|
||||||
|
final profile =
|
||||||
|
_cpInviteCurrentUserIsInviter(data)
|
||||||
|
? _cpInviteReceiverProfileFromData(data)
|
||||||
|
: _cpInviteInviterProfileFromData(data);
|
||||||
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
|
final currentIds =
|
||||||
|
[currentProfile?.id, currentProfile?.account]
|
||||||
|
.map((value) => value?.trim() ?? "")
|
||||||
|
.where((value) => value.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
final profileIds =
|
||||||
|
[profile?.id, profile?.account]
|
||||||
|
.map((value) => value?.trim() ?? "")
|
||||||
|
.where((value) => value.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
if (currentIds.any(profileIds.contains)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isCpBuildInviteData(Map<String, dynamic> data) {
|
||||||
final content = _cpInviteContentMap(data);
|
final content = _cpInviteContentMap(data);
|
||||||
final noticeType =
|
final noticeType =
|
||||||
_firstCpInviteValue([
|
_firstCpInviteValue([
|
||||||
@ -712,17 +827,208 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
content["type"]?.toString(),
|
content["type"]?.toString(),
|
||||||
])?.toUpperCase() ??
|
])?.toUpperCase() ??
|
||||||
"";
|
"";
|
||||||
if (!noticeType.contains(SCSysytemMessageType.CP_BUILD.name)) {
|
return noticeType.contains(SCSysytemMessageType.CP_BUILD.name);
|
||||||
return "";
|
}
|
||||||
}
|
|
||||||
|
bool _cpInviteCurrentUserIsInviter(Map<String, dynamic> data) {
|
||||||
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
|
if (currentProfile == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final currentIds =
|
final currentIds =
|
||||||
<String?>[currentProfile?.id, currentProfile?.account]
|
[currentProfile.id, currentProfile.account]
|
||||||
.map((value) => value?.trim() ?? "")
|
.map((value) => value?.trim() ?? "")
|
||||||
.where((value) => value.isNotEmpty)
|
.where((value) => value.isNotEmpty)
|
||||||
.toSet();
|
.toSet();
|
||||||
final inviterIds = _compactCpInviteValues([
|
if (currentIds.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final receiverIds = _cpInviteReceiverIdentityValues(data);
|
||||||
|
if (currentIds.any(receiverIds.contains)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final inviterIds = _cpInviteInviterIdentityValues(data);
|
||||||
|
return currentIds.any(inviterIds.contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile? _cpInviteInviterProfileFromData(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
) {
|
||||||
|
final content = _cpInviteContentMap(data);
|
||||||
|
final nested = _firstCpInviteMap(data, content, const [
|
||||||
|
"applyUser",
|
||||||
|
"applyUserProfile",
|
||||||
|
"inviter",
|
||||||
|
"inviterProfile",
|
||||||
|
"sender",
|
||||||
|
"senderProfile",
|
||||||
|
"fromUser",
|
||||||
|
"fromUserProfile",
|
||||||
|
"user",
|
||||||
|
"userProfile",
|
||||||
|
"profile",
|
||||||
|
]);
|
||||||
|
return _cpInviteProfileOrNull(
|
||||||
|
id: _firstCpInviteValue([
|
||||||
|
nested["id"]?.toString(),
|
||||||
|
nested["userId"]?.toString(),
|
||||||
|
nested["actualAccount"]?.toString(),
|
||||||
|
data["actualAccount"]?.toString(),
|
||||||
|
data["applyActualAccount"]?.toString(),
|
||||||
|
data["applyUserActualAccount"]?.toString(),
|
||||||
|
data["inviterActualAccount"]?.toString(),
|
||||||
|
data["inviterUserId"]?.toString(),
|
||||||
|
data["inviterId"]?.toString(),
|
||||||
|
data["senderUserId"]?.toString(),
|
||||||
|
data["senderId"]?.toString(),
|
||||||
|
data["fromUserId"]?.toString(),
|
||||||
|
data["applyUserId"]?.toString(),
|
||||||
|
content["applyActualAccount"]?.toString(),
|
||||||
|
content["applyUserActualAccount"]?.toString(),
|
||||||
|
content["inviterActualAccount"]?.toString(),
|
||||||
|
content["inviterUserId"]?.toString(),
|
||||||
|
content["inviterId"]?.toString(),
|
||||||
|
content["senderUserId"]?.toString(),
|
||||||
|
content["senderId"]?.toString(),
|
||||||
|
content["fromUserId"]?.toString(),
|
||||||
|
content["applyUserId"]?.toString(),
|
||||||
|
]),
|
||||||
|
account: _firstCpInviteValue([
|
||||||
|
nested["account"]?.toString(),
|
||||||
|
data["account"]?.toString(),
|
||||||
|
data["applyUserAccount"]?.toString(),
|
||||||
|
content["applyUserAccount"]?.toString(),
|
||||||
|
]),
|
||||||
|
nickname: _firstCpInviteValue([
|
||||||
|
nested["userNickname"]?.toString(),
|
||||||
|
nested["nickname"]?.toString(),
|
||||||
|
data["applyNickname"]?.toString(),
|
||||||
|
data["applyUserNickname"]?.toString(),
|
||||||
|
data["inviterNickname"]?.toString(),
|
||||||
|
data["senderNickname"]?.toString(),
|
||||||
|
data["userNickname"]?.toString(),
|
||||||
|
content["applyNickname"]?.toString(),
|
||||||
|
content["applyUserNickname"]?.toString(),
|
||||||
|
content["inviterNickname"]?.toString(),
|
||||||
|
content["senderNickname"]?.toString(),
|
||||||
|
]),
|
||||||
|
avatar: _firstCpInviteValue([
|
||||||
|
nested["userAvatar"]?.toString(),
|
||||||
|
nested["avatar"]?.toString(),
|
||||||
|
data["applyUserAvatar"]?.toString(),
|
||||||
|
data["inviterAvatar"]?.toString(),
|
||||||
|
data["senderAvatar"]?.toString(),
|
||||||
|
data["userAvatar"]?.toString(),
|
||||||
|
content["applyUserAvatar"]?.toString(),
|
||||||
|
content["inviterAvatar"]?.toString(),
|
||||||
|
content["senderAvatar"]?.toString(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile? _cpInviteReceiverProfileFromData(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
) {
|
||||||
|
final content = _cpInviteContentMap(data);
|
||||||
|
final nested = _firstCpInviteMap(data, content, const [
|
||||||
|
"acceptUser",
|
||||||
|
"acceptUserProfile",
|
||||||
|
"receiver",
|
||||||
|
"receiverProfile",
|
||||||
|
"toUser",
|
||||||
|
"toUserProfile",
|
||||||
|
"targetUser",
|
||||||
|
"targetUserProfile",
|
||||||
|
]);
|
||||||
|
return _cpInviteProfileOrNull(
|
||||||
|
id: _firstCpInviteValue([
|
||||||
|
nested["id"]?.toString(),
|
||||||
|
nested["userId"]?.toString(),
|
||||||
|
nested["actualAccount"]?.toString(),
|
||||||
|
data["acceptActualAccount"]?.toString(),
|
||||||
|
data["acceptUserId"]?.toString(),
|
||||||
|
data["receiverActualAccount"]?.toString(),
|
||||||
|
data["receiverId"]?.toString(),
|
||||||
|
data["receiverUserId"]?.toString(),
|
||||||
|
data["targetUserId"]?.toString(),
|
||||||
|
data["toUserId"]?.toString(),
|
||||||
|
content["acceptActualAccount"]?.toString(),
|
||||||
|
content["acceptUserId"]?.toString(),
|
||||||
|
content["receiverActualAccount"]?.toString(),
|
||||||
|
content["receiverId"]?.toString(),
|
||||||
|
content["receiverUserId"]?.toString(),
|
||||||
|
content["targetUserId"]?.toString(),
|
||||||
|
content["toUserId"]?.toString(),
|
||||||
|
]),
|
||||||
|
account: _firstCpInviteValue([
|
||||||
|
nested["account"]?.toString(),
|
||||||
|
data["acceptAccount"]?.toString(),
|
||||||
|
data["receiverAccount"]?.toString(),
|
||||||
|
content["acceptAccount"]?.toString(),
|
||||||
|
content["receiverAccount"]?.toString(),
|
||||||
|
]),
|
||||||
|
nickname: _firstCpInviteValue([
|
||||||
|
nested["userNickname"]?.toString(),
|
||||||
|
nested["nickname"]?.toString(),
|
||||||
|
data["acceptNickname"]?.toString(),
|
||||||
|
data["acceptUserNickname"]?.toString(),
|
||||||
|
data["receiverNickname"]?.toString(),
|
||||||
|
data["receiverUserNickname"]?.toString(),
|
||||||
|
content["acceptNickname"]?.toString(),
|
||||||
|
content["acceptUserNickname"]?.toString(),
|
||||||
|
content["receiverNickname"]?.toString(),
|
||||||
|
content["receiverUserNickname"]?.toString(),
|
||||||
|
]),
|
||||||
|
avatar: _firstCpInviteValue([
|
||||||
|
nested["userAvatar"]?.toString(),
|
||||||
|
nested["avatar"]?.toString(),
|
||||||
|
data["acceptUserAvatar"]?.toString(),
|
||||||
|
data["receiverAvatar"]?.toString(),
|
||||||
|
data["receiverUserAvatar"]?.toString(),
|
||||||
|
content["acceptUserAvatar"]?.toString(),
|
||||||
|
content["receiverAvatar"]?.toString(),
|
||||||
|
content["receiverUserAvatar"]?.toString(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SocialChatUserProfile? _cpInviteProfileOrNull({
|
||||||
|
String? id,
|
||||||
|
String? account,
|
||||||
|
String? nickname,
|
||||||
|
String? avatar,
|
||||||
|
}) {
|
||||||
|
final hasValue = [id, account, nickname, avatar].any((value) {
|
||||||
|
return (value?.trim() ?? "").isNotEmpty;
|
||||||
|
});
|
||||||
|
if (!hasValue) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return SocialChatUserProfile(
|
||||||
|
id: id?.trim(),
|
||||||
|
account: account?.trim(),
|
||||||
|
userNickname: nickname?.trim(),
|
||||||
|
userAvatar: avatar?.trim(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cpInviteInviterIdFromData(Map<String, dynamic> data) {
|
||||||
|
final profile = _cpInviteInviterProfileFromData(data);
|
||||||
|
return _firstCpInviteValue([profile?.id, profile?.account]) ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String _cpInviteReceiverIdFromData(Map<String, dynamic> data) {
|
||||||
|
final profile = _cpInviteReceiverProfileFromData(data);
|
||||||
|
return _firstCpInviteValue([profile?.id, profile?.account]) ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpInviteInviterIdentityValues(Map<String, dynamic> data) {
|
||||||
|
final content = _cpInviteContentMap(data);
|
||||||
|
return _compactCpInviteValues([
|
||||||
|
data["account"]?.toString(),
|
||||||
data["actualAccount"]?.toString(),
|
data["actualAccount"]?.toString(),
|
||||||
|
data["applyUserId"]?.toString(),
|
||||||
|
data["applyUserAccount"]?.toString(),
|
||||||
data["applyActualAccount"]?.toString(),
|
data["applyActualAccount"]?.toString(),
|
||||||
data["applyUserActualAccount"]?.toString(),
|
data["applyUserActualAccount"]?.toString(),
|
||||||
data["inviterActualAccount"]?.toString(),
|
data["inviterActualAccount"]?.toString(),
|
||||||
@ -731,8 +1037,8 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
data["senderUserId"]?.toString(),
|
data["senderUserId"]?.toString(),
|
||||||
data["senderId"]?.toString(),
|
data["senderId"]?.toString(),
|
||||||
data["fromUserId"]?.toString(),
|
data["fromUserId"]?.toString(),
|
||||||
data["userId"]?.toString(),
|
content["applyUserId"]?.toString(),
|
||||||
content["actualAccount"]?.toString(),
|
content["applyUserAccount"]?.toString(),
|
||||||
content["applyActualAccount"]?.toString(),
|
content["applyActualAccount"]?.toString(),
|
||||||
content["applyUserActualAccount"]?.toString(),
|
content["applyUserActualAccount"]?.toString(),
|
||||||
content["inviterActualAccount"]?.toString(),
|
content["inviterActualAccount"]?.toString(),
|
||||||
@ -741,29 +1047,67 @@ class _ConversationItemState extends State<ConversationItem> {
|
|||||||
content["senderUserId"]?.toString(),
|
content["senderUserId"]?.toString(),
|
||||||
content["senderId"]?.toString(),
|
content["senderId"]?.toString(),
|
||||||
content["fromUserId"]?.toString(),
|
content["fromUserId"]?.toString(),
|
||||||
content["userId"]?.toString(),
|
]).toSet();
|
||||||
data["applyUserId"]?.toString(),
|
}
|
||||||
content["applyUserId"]?.toString(),
|
|
||||||
data["account"]?.toString(),
|
Set<String> _cpInviteReceiverIdentityValues(Map<String, dynamic> data) {
|
||||||
content["account"]?.toString(),
|
final content = _cpInviteContentMap(data);
|
||||||
]);
|
return _compactCpInviteValues([
|
||||||
final isCurrentInviter = currentIds.any(inviterIds.contains);
|
data["acceptUserId"]?.toString(),
|
||||||
final values =
|
data["acceptAccount"]?.toString(),
|
||||||
isCurrentInviter
|
data["acceptActualAccount"]?.toString(),
|
||||||
? [
|
data["receiverId"]?.toString(),
|
||||||
data["acceptActualAccount"]?.toString(),
|
data["receiverUserId"]?.toString(),
|
||||||
data["acceptUserId"]?.toString(),
|
data["receiverAccount"]?.toString(),
|
||||||
data["receiverActualAccount"]?.toString(),
|
data["receiverActualAccount"]?.toString(),
|
||||||
data["receiverId"]?.toString(),
|
data["targetUserId"]?.toString(),
|
||||||
data["receiverUserId"]?.toString(),
|
data["toUserId"]?.toString(),
|
||||||
content["acceptActualAccount"]?.toString(),
|
content["acceptUserId"]?.toString(),
|
||||||
content["acceptUserId"]?.toString(),
|
content["acceptAccount"]?.toString(),
|
||||||
content["receiverActualAccount"]?.toString(),
|
content["acceptActualAccount"]?.toString(),
|
||||||
content["receiverId"]?.toString(),
|
content["receiverId"]?.toString(),
|
||||||
content["receiverUserId"]?.toString(),
|
content["receiverUserId"]?.toString(),
|
||||||
]
|
content["receiverAccount"]?.toString(),
|
||||||
: inviterIds;
|
content["receiverActualAccount"]?.toString(),
|
||||||
return _firstCpInviteValue(values) ?? "";
|
content["targetUserId"]?.toString(),
|
||||||
|
content["toUserId"]?.toString(),
|
||||||
|
]).toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _firstCpInviteMap(
|
||||||
|
Map<String, dynamic> data,
|
||||||
|
Map<String, dynamic> content,
|
||||||
|
Iterable<String> keys,
|
||||||
|
) {
|
||||||
|
for (final key in keys) {
|
||||||
|
final map = _cpInviteStringKeyMap(data[key]);
|
||||||
|
if (map.isNotEmpty) {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
final contentMap = _cpInviteStringKeyMap(content[key]);
|
||||||
|
if (contentMap.isNotEmpty) {
|
||||||
|
return contentMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return const <String, dynamic>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _cpInviteStringKeyMap(dynamic value) {
|
||||||
|
if (value is Map<String, dynamic>) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (value is Map) {
|
||||||
|
return value.map((key, item) => MapEntry(key.toString(), item));
|
||||||
|
}
|
||||||
|
if (value is String && value.trim().isNotEmpty) {
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(value);
|
||||||
|
if (decoded is Map) {
|
||||||
|
return decoded.map((key, item) => MapEntry(key.toString(), item));
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
return const <String, dynamic>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic>? _cpInviteCustomData(V2TimMessage? message) {
|
Map<String, dynamic>? _cpInviteCustomData(V2TimMessage? message) {
|
||||||
|
|||||||
@ -108,6 +108,22 @@ void main() {
|
|||||||
expect(invite?.actualAccount, '2057731345099841537');
|
expect(invite?.actualAccount, '2057731345099841537');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('keeps top-level applicant when content carries receiver profile', () {
|
||||||
|
final invite = SCSystemInvitMessageRes.fromMessageData({
|
||||||
|
'account': '99004',
|
||||||
|
'actualAccount': 'sender-long-id',
|
||||||
|
'relationType': 'CP',
|
||||||
|
'content': jsonEncode({
|
||||||
|
'account': '99005',
|
||||||
|
'actualAccount': 'receiver-long-id',
|
||||||
|
'userNickname': 'Receiver',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(invite?.account, '99004');
|
||||||
|
expect(invite?.actualAccount, 'sender-long-id');
|
||||||
|
});
|
||||||
|
|
||||||
test('infers relation type from gift payload when needed', () {
|
test('infers relation type from gift payload when needed', () {
|
||||||
final invite = SCSystemInvitMessageRes.fromMessageData({
|
final invite = SCSystemInvitMessageRes.fromMessageData({
|
||||||
'content': {
|
'content': {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user