diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6ebc174..76d05b1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -346,15 +346,15 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" SPEC CHECKSUMS: - app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a + app_links: 585674be3c6661708e6cd794ab4f39fb9d8356f9 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f - audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5 - device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 + audioplayers_darwin: f15e209a3e856d1a7edcf98dc029f484fead2242 + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e - firebase_auth: 50af8366c87bb88c80ebeae62eb60189c7246b9b - firebase_core: 995454a784ff288be5689b796deb9e9fa3601818 - firebase_crashlytics: 30dcd6dfd2fe895c0848af46722a4227346c19aa + firebase_auth: 5342db41af2ba5ed32a6177d9e326eecbebda912 + firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c + firebase_crashlytics: 986773c0c7ea4c3f5a4ead141b58522fcd9dfdb1 FirebaseAppCheckInterop: 06fe5a3799278ae4667e6c432edd86b1030fa3df FirebaseAuth: a6575e5fbf46b046c58dc211a28a5fbdd8d4c83b FirebaseAuthInterop: 7087d7a4ee4bc4de019b2d0c240974ed5d89e2fd @@ -366,47 +366,47 @@ SPEC CHECKSUMS: FirebaseRemoteConfigInterop: 1c6135e8a094cc6368949f5faeeca7ee8948b8aa FirebaseSessions: b9a92c1c51bbb81e78fc3142cda6d925d700f8e7 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 - flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1 - fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 - google_sign_in_ios: b48bb9af78576358a168361173155596c845f0b9 + flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e + fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f + google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063 - image_cropper: 655b3ba703c9e15e3111e79151624d6154288774 - image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 - in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8 + image_cropper: a3291c624a953049bc6a02e1f8c8ceb162a24b25 + image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b + in_app_purchase_storekit: 2342c0a5da86593124d08dd13d920f39a52b273a libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - on_audio_query_ios: 28a780e2d0d85d92d500ba6e12c6c8167022b2fa - package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 - pag: f01aa9017ab0e04a83ba4a20d6070a50f0ac9da8 - permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d + on_audio_query_ios: e5afb0d4b42cfa3e8762d8ec1953540cb4f02100 + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + pag: ff30ba5e4be59380a5ddac6b183905729be15a3c + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377 - shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb - sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418 - sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 + sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 - tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68 - tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790 - tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc + tancent_vap: eceaaf095167d102727cf799c75891057834c311 + tencent_cloud_chat_sdk: 546bb906bbe585814e3d2d33afdb91f09c0eac44 + tencent_rtc_sdk: 1ca5201f7bddc691a3a95486325ad6dc792a273d TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 TXCustomBeautyProcesserPlugin: 099393b941cb40eda12b3a80bf6c0319957b1cfd TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2 TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c - url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b - video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52 - video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140 - wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 - webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d + url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa + video_player_avfoundation: e54d03b3b19f64f2c557354f72ce538387541ba6 + video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 + wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e + webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4 PODFILE CHECKSUM: a6f49a93e5f85201a2efdadcd7cf184b0e310894 diff --git a/lib/app/config/configs/sc_variant1_config.dart b/lib/app/config/configs/sc_variant1_config.dart index 221ce48..c252075 100644 --- a/lib/app/config/configs/sc_variant1_config.dart +++ b/lib/app/config/configs/sc_variant1_config.dart @@ -16,8 +16,8 @@ class SCVariant1Config implements AppConfig { @override String get apiHost => const String.fromEnvironment( 'API_HOST', - defaultValue: 'http://192.168.110.64:1100/', - ); // 默认连线上环境,本地调试可通过 --dart-define=API_HOST 覆盖 本地“http://192.168.110.66:1100/”,线上“https://jvapi.haiyihy.com/” + defaultValue: 'http://192.168.110.66:1100/', + ); // 默认连本地接口,本地调试可通过 --dart-define=API_HOST 覆盖,线上“https://jvapi.haiyihy.com/” @override String get imgHost => 'https://img.atuchat.com/'; // 测试图片服务器,上架前需替换为正式域名 diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart index f26efd1..cb70688 100644 --- a/lib/services/audio/rtc_manager.dart +++ b/lib/services/audio/rtc_manager.dart @@ -10,6 +10,7 @@ import 'package:yumi/shared/tools/sc_permission_utils.dart'; import 'package:yumi/shared/tools/sc_room_utils.dart'; import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart'; import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart'; +import 'package:yumi/shared/tools/sc_room_rocket_reward_popup_ack.dart'; import 'package:yumi/shared/data_sources/models/message/room_rocket_launch_broadcast_message.dart'; import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart'; import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart'; @@ -3521,21 +3522,22 @@ class RealTimeCommunicationManager extends ChangeNotifier { SCChatRoomRepository() .roomRocketRewardPopups(roomId) .then((res) { - if (res.records.isEmpty || + final popupRecords = SCRoomRocketRewardPopupAck.filterUnshownRecords( + res.records, + ); + if (popupRecords.isEmpty || roomId != (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) { return; } - final recordIds = res.records - .map((item) => item.id) - .where((item) => item.isNotEmpty) - .toList(growable: false); if (SCRoomTopLayerGuard().isActive) { SCRoomTopLayerGuard().runWhenInactive(() { _loadRoomRocketRewardPopups(roomId); }); return; } + SCRoomRocketRewardPopupAck.markShownAndAck(popupRecords); + final recordIds = SCRoomRocketRewardPopupAck.recordIds(popupRecords); SmartDialog.dismiss(tag: _roomRocketRewardDialogTag); SmartDialog.show( tag: _roomRocketRewardDialogTag, @@ -3550,16 +3552,14 @@ class RealTimeCommunicationManager extends ChangeNotifier { keepDialogTags: const {_roomRocketRewardDialogTag}, child: RoomRocketRewardDialogLoader( roomId: roomId, - initialPopupRecords: res.records, + initialPopupRecords: popupRecords, userProfileResolver: roomRocketRewardUserProfileForRecord, onClose: () { SmartDialog.dismiss(tag: _roomRocketRewardDialogTag); scheduleRoomRocketPostLaunchStatusRefresh(roomId: roomId); if (recordIds.isNotEmpty) { - unawaited( - SCChatRoomRepository().ackRoomRocketRewardPopups( - recordIds, - ), + SCRoomRocketRewardPopupAck.markShownAndAck( + popupRecords, ); } }, diff --git a/lib/services/audio/rtm_manager.dart b/lib/services/audio/rtm_manager.dart index 31f8cf3..300dd73 100644 --- a/lib/services/audio/rtm_manager.dart +++ b/lib/services/audio/rtm_manager.dart @@ -16,9 +16,11 @@ import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart'; import 'package:yumi/shared/tools/sc_room_utils.dart'; import 'package:yumi/shared/tools/sc_cp_relation_broadcast_assets.dart'; import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart'; +import 'package:yumi/shared/tools/sc_room_rocket_reward_popup_ack.dart'; import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:provider/provider.dart'; import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart'; @@ -911,13 +913,6 @@ class RealTimeMessagingManager extends ChangeNotifier { } final applyId = _cpInviteApplyIdFromData(data); final invite = _cpInviteFromMessageData(data); - final messageKey = applyId.isNotEmpty ? applyId : message?.msgID ?? ""; - if (messageKey.isNotEmpty && !_shownCpInviteMessageKeys.add(messageKey)) { - _cpInviteLog( - 'skip duplicated CP_BUILD applyId=$applyId msgId=${message?.msgID}', - ); - return; - } if (applyId.isEmpty || invite == null) { _cpInviteLog( 'skip CP_BUILD reason=missing_apply_or_content ' @@ -927,6 +922,16 @@ class RealTimeMessagingManager extends ChangeNotifier { ); return; } + if (!await _isCpInviteApplyWaitingForAutoPopup(applyId)) { + return; + } + final messageKey = applyId.isNotEmpty ? applyId : message?.msgID ?? ""; + if (messageKey.isNotEmpty && !_shownCpInviteMessageKeys.add(messageKey)) { + _cpInviteLog( + 'skip duplicated CP_BUILD applyId=$applyId msgId=${message?.msgID}', + ); + return; + } final context = navigatorKey.currentState?.context; final currentProfile = AccountStorage().getCurrentUser()?.userProfile; if (context == null || currentProfile == null) { @@ -935,6 +940,10 @@ class RealTimeMessagingManager extends ChangeNotifier { ); return; } + if (!context.mounted) { + _cpInviteLog('skip CP_BUILD reason=context_unmounted applyId=$applyId'); + return; + } if (_cpInviteTargetsCurrentAsSenderAndReceiver( invite, currentProfile, @@ -1095,6 +1104,29 @@ class RealTimeMessagingManager extends ChangeNotifier { ""; } + Future _isCpInviteApplyWaitingForAutoPopup(String applyId) async { + try { + final status = await SCAccountRepository().cpRelationshipApplyStatus( + applyId, + ); + final normalizedStatus = status?.trim().toUpperCase() ?? ""; + if (normalizedStatus == "WAIT") { + return true; + } + _cpInviteLog( + 'skip CP_BUILD reason=apply_status_not_wait ' + 'applyId=$applyId status=$normalizedStatus', + ); + return false; + } catch (error) { + _cpInviteLog( + 'skip CP_BUILD reason=apply_status_query_failed ' + 'applyId=$applyId error=$error', + ); + return false; + } + } + bool _handleCpInviteResultMessage( Map data, { String? msgId, @@ -1650,32 +1682,32 @@ class RealTimeMessagingManager extends ChangeNotifier { ) { final content = _cpInviteContentMap(data); return _firstCpInviteValue([ - data["actualAccount"]?.toString(), - data["applyActualAccount"]?.toString(), - data["applyUserActualAccount"]?.toString(), - data["inviterActualAccount"]?.toString(), + data["userId"]?.toString(), data["inviterUserId"]?.toString(), data["inviterId"]?.toString(), data["senderUserId"]?.toString(), data["senderId"]?.toString(), data["fromUserId"]?.toString(), data["applyUserId"]?.toString(), - data["account"]?.toString(), - content["applyActualAccount"]?.toString(), - content["applyUserActualAccount"]?.toString(), - content["inviterActualAccount"]?.toString(), + content["userId"]?.toString(), content["inviterUserId"]?.toString(), content["inviterId"]?.toString(), content["senderUserId"]?.toString(), content["senderId"]?.toString(), content["fromUserId"]?.toString(), content["applyUserId"]?.toString(), + data["actualAccount"]?.toString(), + data["applyActualAccount"]?.toString(), + data["applyUserActualAccount"]?.toString(), + data["inviterActualAccount"]?.toString(), + data["account"]?.toString(), + content["applyActualAccount"]?.toString(), + content["applyUserActualAccount"]?.toString(), + content["inviterActualAccount"]?.toString(), content["applyUserAccount"]?.toString(), invite.actualAccount, invite.account, - data["userId"]?.toString(), content["actualAccount"]?.toString(), - content["userId"]?.toString(), content["account"]?.toString(), ]) ?? ""; @@ -1684,20 +1716,20 @@ class RealTimeMessagingManager extends ChangeNotifier { String _cpInviteReceiverIdFromMessageData(Map data) { final content = _cpInviteContentMap(data); return _firstCpInviteValue([ - 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(), + data["acceptActualAccount"]?.toString(), + data["receiverActualAccount"]?.toString(), + content["acceptActualAccount"]?.toString(), + content["receiverActualAccount"]?.toString(), ]) ?? ""; } @@ -2657,39 +2689,19 @@ class RealTimeMessagingManager extends ChangeNotifier { } 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 = [ + return scBuildCpRelationNoticeDedupKey( + action: action, + groupId: msg.groupId, + relationType: relationType, + explicitKey: msg.giftBatchId, + userIds: [_roomMsgUserId(msg.user), _roomMsgUserId(msg.toUser)], + userNames: [ _roomMsgUserDisplayName(msg.user), _roomMsgUserDisplayName(msg.toUser), - ]..sort(); - ids.addAll(names); - } - return [ - "cp_notice", - action, - msg.groupId ?? "", - relationType, - ...ids, - ].join("|"); + ], + ); } bool _isLiveCpRelationBroadcastMessage(Msg msg) { @@ -4557,12 +4569,19 @@ class RealTimeMessagingManager extends ChangeNotifier { ); return; } + final popupRecords = SCRoomRocketRewardPopupAck.filterUnshownRecords( + res.records, + ); + if (res.records.isNotEmpty && popupRecords.isEmpty) { + return; + } + if (popupRecords.isEmpty && initialRoomRecords.isEmpty) { + return; + } _roomRocketRewardPopupRetryTimer?.cancel(); _roomRocketRewardPopupRetryTimer = null; - final recordIds = res.records - .map((item) => item.id) - .where((item) => item.isNotEmpty) - .toList(growable: false); + SCRoomRocketRewardPopupAck.markShownAndAck(popupRecords); + final recordIds = SCRoomRocketRewardPopupAck.recordIds(popupRecords); final userProfileResolver = _roomRocketRewardUserProfileResolver(); SmartDialog.dismiss(tag: _roomRocketRewardDialogTag); SmartDialog.show( @@ -4578,18 +4597,14 @@ class RealTimeMessagingManager extends ChangeNotifier { keepDialogTags: const {_roomRocketRewardDialogTag}, child: RoomRocketRewardDialogLoader( roomId: roomId, - initialPopupRecords: res.records, + initialPopupRecords: popupRecords, initialRoomRecords: initialRoomRecords, userProfileResolver: userProfileResolver, onClose: () { SmartDialog.dismiss(tag: _roomRocketRewardDialogTag); _scheduleRoomRocketPostLaunchStatusRefresh(roomId); if (recordIds.isNotEmpty) { - unawaited( - SCChatRoomRepository().ackRoomRocketRewardPopups( - recordIds, - ), - ); + SCRoomRocketRewardPopupAck.markShownAndAck(popupRecords); } }, ), diff --git a/lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart b/lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart index 928d10a..59ff854 100644 --- a/lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart +++ b/lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart @@ -1387,6 +1387,26 @@ class SCAccountRepository implements SocialChatUserRepository { } } + ///user/cp-relationship/apply-status + Future cpRelationshipApplyStatus(String applyId) async { + final query = {"applyId": applyId}; + _cpDebugLog('applyStatus request query=$query'); + try { + final result = await http.get( + "/user/cp-relationship/apply-status", + queryParams: query, + allowNullBody: true, + fromJson: (json) => json?.toString(), + ); + final normalizedResult = result?.isEmpty == true ? null : result; + _cpDebugLog('applyStatus response query=$query status=$normalizedResult'); + return normalizedResult; + } catch (error) { + _cpDebugLog('applyStatus failed query=$query error=$error'); + rethrow; + } + } + ///user/cp/cabin @override Future cpCabin( diff --git a/lib/shared/tools/sc_cp_relation_notice_utils.dart b/lib/shared/tools/sc_cp_relation_notice_utils.dart index fb544d1..c1d97b1 100644 --- a/lib/shared/tools/sc_cp_relation_notice_utils.dart +++ b/lib/shared/tools/sc_cp_relation_notice_utils.dart @@ -76,6 +76,62 @@ String scCpRelationDisplayDays(String? days) { return displayValue.toString(); } +String scBuildCpRelationNoticeDedupKey({ + required String? action, + required String? groupId, + required String? relationType, + String? explicitKey, + Iterable userIds = const [], + Iterable userNames = const [], +}) { + final normalizedAction = action?.trim() ?? ""; + final normalizedGroupId = groupId?.trim() ?? ""; + final normalizedRelationType = scNormalizeCpRelationType(relationType); + final normalizedExplicitKey = explicitKey?.trim() ?? ""; + if (normalizedExplicitKey.isNotEmpty) { + return [ + "cp_notice", + normalizedAction, + normalizedGroupId, + normalizedRelationType, + normalizedExplicitKey, + ].join("|"); + } + + final ids = + userIds + .map((value) => value?.trim() ?? "") + .where((value) => value.isNotEmpty) + .toList() + ..sort(); + if (ids.isNotEmpty) { + return [ + "cp_notice", + normalizedAction, + normalizedGroupId, + normalizedRelationType, + ...ids, + ].join("|"); + } + + final names = + userNames + .map((value) => value?.trim() ?? "") + .where((value) => value.isNotEmpty) + .toList() + ..sort(); + if (names.isEmpty) { + return ""; + } + return [ + "cp_notice", + normalizedAction, + normalizedGroupId, + normalizedRelationType, + ...names, + ].join("|"); +} + String scBuildCpRelationEstablishedNotice({ required String userName1, required String userName2, diff --git a/lib/shared/tools/sc_room_rocket_reward_popup_ack.dart b/lib/shared/tools/sc_room_rocket_reward_popup_ack.dart new file mode 100644 index 0000000..50c8427 --- /dev/null +++ b/lib/shared/tools/sc_room_rocket_reward_popup_ack.dart @@ -0,0 +1,45 @@ +import 'dart:async'; + +import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart'; + +class SCRoomRocketRewardPopupAck { + static final Set _shownRecordIds = {}; + + static List filterUnshownRecords( + List records, + ) { + return records + .where((record) { + final id = record.id.trim(); + return id.isEmpty || !_shownRecordIds.contains(id); + }) + .toList(growable: false); + } + + static List recordIds(List records) { + final ids = []; + final seen = {}; + for (final record in records) { + final id = record.id.trim(); + if (id.isEmpty || !seen.add(id)) { + continue; + } + ids.add(id); + } + return ids; + } + + static void markShownAndAck(List records) { + final ids = recordIds(records); + if (ids.isEmpty) { + return; + } + _shownRecordIds.addAll(ids); + unawaited( + SCChatRoomRepository() + .ackRoomRocketRewardPopups(ids) + .catchError((_) => false), + ); + } +} diff --git a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart index 848d30f..2c72d1c 100644 --- a/lib/ui_kit/widgets/msg/message_conversation_list_page.dart +++ b/lib/ui_kit/widgets/msg/message_conversation_list_page.dart @@ -872,26 +872,28 @@ class _ConversationItemState extends State { id: _firstCpInviteValue([ nested["id"]?.toString(), nested["userId"]?.toString(), - nested["actualAccount"]?.toString(), - data["actualAccount"]?.toString(), - data["applyActualAccount"]?.toString(), - data["applyUserActualAccount"]?.toString(), - data["inviterActualAccount"]?.toString(), + data["userId"]?.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["userId"]?.toString(), content["inviterUserId"]?.toString(), content["inviterId"]?.toString(), content["senderUserId"]?.toString(), content["senderId"]?.toString(), content["fromUserId"]?.toString(), content["applyUserId"]?.toString(), + nested["actualAccount"]?.toString(), + data["actualAccount"]?.toString(), + data["applyActualAccount"]?.toString(), + data["applyUserActualAccount"]?.toString(), + data["inviterActualAccount"]?.toString(), + content["applyActualAccount"]?.toString(), + content["applyUserActualAccount"]?.toString(), + content["inviterActualAccount"]?.toString(), ]), account: _firstCpInviteValue([ nested["account"]?.toString(), @@ -944,21 +946,21 @@ class _ConversationItemState extends State { 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(), + nested["actualAccount"]?.toString(), + data["acceptActualAccount"]?.toString(), + data["receiverActualAccount"]?.toString(), + content["acceptActualAccount"]?.toString(), + content["receiverActualAccount"]?.toString(), ]), account: _firstCpInviteValue([ nested["account"]?.toString(), diff --git a/test/gift_res_cp_relation_type_test.dart b/test/gift_res_cp_relation_type_test.dart index aad6705..d4fe46b 100644 --- a/test/gift_res_cp_relation_type_test.dart +++ b/test/gift_res_cp_relation_type_test.dart @@ -51,6 +51,25 @@ void main() { "A's cp B has joined the party", ); }); + + test('relation notice dedup prefers apply id over user fields', () { + final leftKey = scBuildCpRelationNoticeDedupKey( + action: 'RELATION_ESTABLISHED', + groupId: 'room-1', + relationType: 'BROTHER', + explicitKey: '3001', + userIds: const ['1001', '2001'], + ); + final rightKey = scBuildCpRelationNoticeDedupKey( + action: 'RELATION_ESTABLISHED', + groupId: 'room-1', + relationType: 'BROTHERS', + explicitKey: '3001', + userIds: const ['1001-account', '2001-account'], + ); + + expect(rightKey, leftKey); + }); }); group('scCpRelationTypeForGift', () {