Merge branch 'feature' of gitea.haiyihy.com:hy/chatapp3-flutter into feature

This commit is contained in:
zhx 2026-05-25 19:55:54 +08:00
commit a73e2ff14b
13 changed files with 210 additions and 177 deletions

View File

@ -346,15 +346,15 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin" :path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
SPEC CHECKSUMS: SPEC CHECKSUMS:
app_links: 585674be3c6661708e6cd794ab4f39fb9d8356f9 app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
audioplayers_darwin: f15e209a3e856d1a7edcf98dc029f484fead2242 audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
firebase_auth: 5342db41af2ba5ed32a6177d9e326eecbebda912 firebase_auth: 50af8366c87bb88c80ebeae62eb60189c7246b9b
firebase_core: 99a37263b3c27536063a7b601d9e2a49400a433c firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
firebase_crashlytics: 986773c0c7ea4c3f5a4ead141b58522fcd9dfdb1 firebase_crashlytics: 30dcd6dfd2fe895c0848af46722a4227346c19aa
FirebaseAppCheckInterop: 06fe5a3799278ae4667e6c432edd86b1030fa3df FirebaseAppCheckInterop: 06fe5a3799278ae4667e6c432edd86b1030fa3df
FirebaseAuth: a6575e5fbf46b046c58dc211a28a5fbdd8d4c83b FirebaseAuth: a6575e5fbf46b046c58dc211a28a5fbdd8d4c83b
FirebaseAuthInterop: 7087d7a4ee4bc4de019b2d0c240974ed5d89e2fd FirebaseAuthInterop: 7087d7a4ee4bc4de019b2d0c240974ed5d89e2fd
@ -366,47 +366,47 @@ SPEC CHECKSUMS:
FirebaseRemoteConfigInterop: 1c6135e8a094cc6368949f5faeeca7ee8948b8aa FirebaseRemoteConfigInterop: 1c6135e8a094cc6368949f5faeeca7ee8948b8aa
FirebaseSessions: b9a92c1c51bbb81e78fc3142cda6d925d700f8e7 FirebaseSessions: b9a92c1c51bbb81e78fc3142cda6d925d700f8e7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017 google_sign_in_ios: b48bb9af78576358a168361173155596c845f0b9
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063 HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
image_cropper: a3291c624a953049bc6a02e1f8c8ceb162a24b25 image_cropper: 655b3ba703c9e15e3111e79151624d6154288774
image_picker_ios: 4f2f91b01abdb52842a8e277617df877e40f905b image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
in_app_purchase_storekit: 2342c0a5da86593124d08dd13d920f39a52b273a in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4 libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
on_audio_query_ios: e5afb0d4b42cfa3e8762d8ec1953540cb4f02100 on_audio_query_ios: 28a780e2d0d85d92d500ba6e12c6c8167022b2fa
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
pag: ff30ba5e4be59380a5ddac6b183905729be15a3c pag: f01aa9017ab0e04a83ba4a20d6070a50f0ac9da8
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377 SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
tancent_vap: eceaaf095167d102727cf799c75891057834c311 tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68
tencent_cloud_chat_sdk: 546bb906bbe585814e3d2d33afdb91f09c0eac44 tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790
tencent_rtc_sdk: 1ca5201f7bddc691a3a95486325ad6dc792a273d tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
TXCustomBeautyProcesserPlugin: 099393b941cb40eda12b3a80bf6c0319957b1cfd TXCustomBeautyProcesserPlugin: 099393b941cb40eda12b3a80bf6c0319957b1cfd
TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2 TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2
TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
video_player_avfoundation: e54d03b3b19f64f2c557354f72ce538387541ba6 video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52
video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
webview_flutter_wkwebview: 29eb20d43355b48fe7d07113835b9128f84e3af4 webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
PODFILE CHECKSUM: a6f49a93e5f85201a2efdadcd7cf184b0e310894 PODFILE CHECKSUM: a6f49a93e5f85201a2efdadcd7cf184b0e310894

View File

@ -19,7 +19,6 @@ import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart'; 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/data_sources/sources/repositories/sc_user_repository_impl.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/main.dart'; import 'package:yumi/main.dart';
@ -1520,8 +1519,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
'giftType=$giftType', 'giftType=$giftType',
); );
await _showCpGiftRelationCheckToastIfNeeded(request);
try { try {
final repository = SCChatRoomRepository(); final repository = SCChatRoomRepository();
_giftFxLog( _giftFxLog(
@ -1728,49 +1725,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
); );
} }
Future<void> _showCpGiftRelationCheckToastIfNeeded(
_GiftSendRequest request,
) async {
final relationType = request.cpRelationType;
if (relationType == null || relationType.trim().isEmpty) {
return;
}
final acceptUserId = request.acceptUserIds
.map((userId) => userId.trim())
.firstWhere((userId) => userId.isNotEmpty, orElse: () => '');
if (acceptUserId.isEmpty) {
return;
}
try {
final result = await SCAccountRepository().cpGiftRelationCheck(
acceptUserId: acceptUserId,
relationType: relationType,
);
final message = result.msg.trim();
_giftFxLog(
'cp gift relation check '
'acceptUserId=$acceptUserId '
'relationType=$relationType '
'status=${result.status} '
'msg=$message '
'userCount=${result.userRelationCount}/${result.maxRelationCount} '
'acceptCount=${result.acceptUserRelationCount}/${result.maxRelationCount}',
);
if (!result.status &&
message.isNotEmpty &&
message.toUpperCase() != 'OK') {
SCTts.show(message);
}
} catch (error) {
_giftFxLog(
'cp gift relation check skipped '
'acceptUserId=$acceptUserId '
'relationType=$relationType '
'error=$error',
);
}
}
Future<List<MicRes>> _giveBackpackGiftToRecipients( Future<List<MicRes>> _giveBackpackGiftToRecipients(
SCChatRoomRepository repository, SCChatRoomRepository repository,
_GiftSendRequest request, _GiftSendRequest request,

View File

@ -7,7 +7,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/app_localizations.dart'; import 'package:yumi/app_localizations.dart';
import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/main.dart' show modalRouteObserver; import 'package:yumi/main.dart' show routeObserver;
import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/components/sc_compontent.dart';
import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/ui_kit/widgets/room/room_bottom_widget.dart'; import 'package:yumi/ui_kit/widgets/room/room_bottom_widget.dart';
@ -83,7 +83,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
bool _roomProviderRebuildScheduled = false; bool _roomProviderRebuildScheduled = false;
bool _roomRouteVisibleScheduled = false; bool _roomRouteVisibleScheduled = false;
bool _roomVisualEffectsEnsureScheduled = false; bool _roomVisualEffectsEnsureScheduled = false;
ModalRoute<dynamic>? _routeObserverRoute; PageRoute<dynamic>? _routeObserverRoute;
SCRoomTopLayerGuardLease? _coveredRouteGuardLease; SCRoomTopLayerGuardLease? _coveredRouteGuardLease;
bool _roomRouteVisible = false; bool _roomRouteVisible = false;
@ -91,9 +91,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
void initState() { void initState() {
super.initState(); super.initState();
_tabController = TabController(length: _chatTabCount, vsync: this); _tabController = TabController(length: _chatTabCount, vsync: this);
SCRoomTopLayerGuard().activeListenable.addListener(
_handleRoomTopLayerGuardChanged,
);
_tabController.addListener(_handleTabChange); _tabController.addListener(_handleTabChange);
_subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen(( _subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen((
event, event,
@ -123,12 +120,12 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
context.read<RoomMusicManager>().attachRtcProvider(rtcProvider); context.read<RoomMusicManager>().attachRtcProvider(rtcProvider);
} }
final route = ModalRoute.of(context); final route = ModalRoute.of(context);
if (route != null && _routeObserverRoute != route) { if (route is PageRoute<dynamic> && _routeObserverRoute != route) {
if (_routeObserverRoute != null) { if (_routeObserverRoute != null) {
modalRouteObserver.unsubscribe(this); routeObserver.unsubscribe(this);
} }
_routeObserverRoute = route; _routeObserverRoute = route;
modalRouteObserver.subscribe(this, route); routeObserver.subscribe(this, route);
if (route.isCurrent) { if (route.isCurrent) {
_scheduleRoomRouteVisible(); _scheduleRoomRouteVisible();
} else { } else {
@ -140,10 +137,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
@override @override
void dispose() { void dispose() {
SCRoomTopLayerGuard().activeListenable.removeListener( routeObserver.unsubscribe(this);
_handleRoomTopLayerGuardChanged,
);
modalRouteObserver.unsubscribe(this);
_routeObserverRoute = null; _routeObserverRoute = null;
_releaseCoveredRouteGuard(); _releaseCoveredRouteGuard();
_setRoomRouteVisible(false); _setRoomRouteVisible(false);
@ -215,13 +209,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
_coveredRouteGuardLease = null; _coveredRouteGuardLease = null;
} }
void _handleRoomTopLayerGuardChanged() {
if (!SCRoomTopLayerGuard().isActive || !mounted) {
return;
}
_clearTransientRoomVisualEffects(reason: 'room_top_layer_guard');
}
void _setRoomRouteVisible(bool visible) { void _setRoomRouteVisible(bool visible) {
if (_roomRouteVisible == visible) { if (_roomRouteVisible == visible) {
return; return;

View File

@ -1588,7 +1588,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
if (payload.rightUserId.trim().isEmpty) { if (payload.rightUserId.trim().isEmpty) {
return; return;
} }
const cancelText = 'unbing'; const cancelText = 'unbind';
SCLoadingManager.show(context: context); SCLoadingManager.show(context: context);
late final _ProfileRelationProgressData progress; late final _ProfileRelationProgressData progress;
try { try {
@ -1629,7 +1629,10 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
Future<_ProfileRelationProgressData> _loadProfileRelationProgressData( Future<_ProfileRelationProgressData> _loadProfileRelationProgressData(
_ProfileRelationProgressPayload payload, _ProfileRelationProgressPayload payload,
) async { ) async {
final partnerUserId = payload.rightUserId.trim(); final partnerUserId = _counterpartUserIdForCurrentUser(
leftUserId: payload.leftUserId,
rightUserId: payload.rightUserId,
);
final fallback = _ProfileRelationProgressData.fallback(payload); final fallback = _ProfileRelationProgressData.fallback(payload);
if (partnerUserId.isEmpty) { if (partnerUserId.isEmpty) {
return fallback; return fallback;
@ -1666,11 +1669,34 @@ class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
} }
} }
String _counterpartUserIdForCurrentUser({
required String leftUserId,
required String rightUserId,
}) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
final leftId = leftUserId.trim();
final rightId = rightUserId.trim();
if (currentUserId.isEmpty) {
return rightId;
}
if (currentUserId == leftId) {
return rightId;
}
if (currentUserId == rightId) {
return leftId;
}
return rightId;
}
Future<void> _showProfileRelationDismissConfirm( Future<void> _showProfileRelationDismissConfirm(
_ProfileRelationProgressPayload payload, { _ProfileRelationProgressPayload payload, {
int? dismissCost, int? dismissCost,
}) async { }) async {
final partnerUserId = payload.rightUserId.trim(); final partnerUserId = _counterpartUserIdForCurrentUser(
leftUserId: payload.leftUserId,
rightUserId: payload.rightUserId,
);
if (partnerUserId.isEmpty) { if (partnerUserId.isEmpty) {
return; return;
} }

View File

@ -544,10 +544,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
int? get previewRoomSeatCount => _previewRoomSeatCount; int? get previewRoomSeatCount => _previewRoomSeatCount;
bool get shouldShowRoomVisualEffects => bool get shouldShowRoomVisualEffects =>
currenRoom != null && currenRoom != null && _roomVisualEffectsEnabled && _voiceRoomRouteVisible;
_roomVisualEffectsEnabled &&
_voiceRoomRouteVisible &&
!SCRoomTopLayerGuard().isActive;
void _startVoiceRoomForegroundService() { void _startVoiceRoomForegroundService() {
final roomProfile = currenRoom?.roomProfile?.roomProfile; final roomProfile = currenRoom?.roomProfile?.roomProfile;

View File

@ -3100,7 +3100,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
} }
} }
void _enqueueCpRelationBroadcastFloatingIfNeeded(Msg msg) { void _enqueueCpRelationBroadcastFloatingIfNeeded(
Msg msg, {
bool isRegionBroadcast = false,
}) {
if (msg.type != SCRoomMsgType.cpSystemNotice) { if (msg.type != SCRoomMsgType.cpSystemNotice) {
return; return;
} }
@ -3108,8 +3111,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (action != "RELATION_ESTABLISHED" && action != "RELATION_JOINED_ROOM") { if (action != "RELATION_ESTABLISHED" && action != "RELATION_JOINED_ROOM") {
return; return;
} }
if (!_isLiveCpRelationBroadcastMessage(msg) || if (!_isLiveCpRelationBroadcastMessage(msg)) {
!_shouldShowCpRelationBroadcastFloating(msg.groupId ?? "")) {
return; return;
} }
final relationType = scNormalizeCpRelationType(msg.role); final relationType = scNormalizeCpRelationType(msg.role);
@ -3132,6 +3134,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
giftTab: relationType, giftTab: relationType,
displayText: msg.msg ?? "", displayText: msg.msg ?? "",
durationSeconds: 5, durationSeconds: 5,
broadcastScope:
isRegionBroadcast ? SCFloatingMessage.broadcastScopeRegion : '',
), ),
); );
} }
@ -3187,23 +3191,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
return DateTime.now().millisecondsSinceEpoch - timestamp < 60000; return DateTime.now().millisecondsSinceEpoch - timestamp < 60000;
} }
bool _shouldShowCpRelationBroadcastFloating(String groupId) {
final normalizedGroupId = groupId.trim();
final currentContext = context;
if (normalizedGroupId.isEmpty ||
currentContext == null ||
!currentContext.mounted ||
!_isCurrentVoiceRoomGroup(normalizedGroupId)) {
return false;
}
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
currentContext,
listen: false,
);
return rtcProvider.shouldShowRoomVisualEffects &&
rtcProvider.isVoiceRoomRouteVisible;
}
String _roomMsgUserId(SocialChatUserProfile? profile) { String _roomMsgUserId(SocialChatUserProfile? profile) {
return _firstCpInviteValue([profile?.id, profile?.account]) ?? ""; return _firstCpInviteValue([profile?.id, profile?.account]) ?? "";
} }
@ -6044,6 +6031,34 @@ class RealTimeMessagingManager extends ChangeNotifier {
if (isRegionBroadcastGroup) { if (isRegionBroadcastGroup) {
_handleRegionGiftBroadcast(data["data"]); _handleRegionGiftBroadcast(data["data"]);
} }
} else if (type == SCRoomMsgType.cpSystemNotice) {
final payload = data["data"] ?? data;
final payloadMap = _broadcastPayloadMap(payload);
if (payloadMap.isEmpty) {
return;
}
final msg = Msg.fromJson(payloadMap);
msg.type ??= SCRoomMsgType.cpSystemNotice;
msg.groupId = _firstNonBlank([
msg.groupId,
_payloadText(payloadMap["groupId"]),
_payloadText(payloadMap["roomId"]),
_payloadText(data["groupId"]),
_payloadText(data["roomId"]),
]);
msg.noticeAction ??= data["noticeAction"]?.toString();
if ((msg.role ?? "").trim().isEmpty) {
msg.role = _firstNonBlank([
_payloadText(payloadMap["role"]),
_payloadText(payloadMap["relationType"]),
_payloadText(data["role"]),
_payloadText(data["relationType"]),
]);
}
_enqueueCpRelationBroadcastFloatingIfNeeded(
msg,
isRegionBroadcast: isRegionBroadcastGroup,
);
} else if (type == SCRoomMsgType.inviteRoom) { } else if (type == SCRoomMsgType.inviteRoom) {
/// ///
var fdata = data["data"]; var fdata = data["data"];

View File

@ -631,10 +631,7 @@ class SocialChatUserProfileManager extends ChangeNotifier {
required String relationType, required String relationType,
String? fallbackText, String? fallbackText,
}) async { }) async {
if (!_isCurrentUserId(userId)) { final partnerUserId = _cpPartnerUserIdForCurrentUser(cpPair);
return null;
}
final partnerUserId = _cpPartnerUserId(userId, cpPair);
if (partnerUserId.isEmpty) { if (partnerUserId.isEmpty) {
return null; return null;
} }
@ -659,14 +656,21 @@ class SocialChatUserProfileManager extends ChangeNotifier {
} }
} }
String _cpPartnerUserId(String userId, CPRes cpPair) { String _cpPartnerUserIdForCurrentUser(CPRes cpPair) {
final currentId = userId.trim(); final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
if (currentUserId.isEmpty) {
return "";
}
final meUserId = cpPair.meUserId?.trim() ?? ""; final meUserId = cpPair.meUserId?.trim() ?? "";
final cpUserId = cpPair.cpUserId?.trim() ?? ""; final cpUserId = cpPair.cpUserId?.trim() ?? "";
if (currentId.isNotEmpty && currentId == cpUserId) { if (currentUserId == meUserId) {
return cpUserId;
}
if (currentUserId == cpUserId) {
return meUserId; return meUserId;
} }
return cpUserId; return "";
} }
bool _hasCpPair(CPRes? pair) { bool _hasCpPair(CPRes? pair) {

View File

@ -803,10 +803,22 @@ class OverlayManager {
return message.type == 2 && message.isRegionBroadcast; return message.type == 2 && message.isRegionBroadcast;
} }
bool _isRegionCpGiftMessage(SCFloatingMessage message) {
return message.type == 1 &&
message.isRegionBroadcast &&
scIsCpGiftTab(message.giftTab);
}
bool _isRegionCpRelationMessage(SCFloatingMessage message) {
return message.type == 6 && message.isRegionBroadcast;
}
bool _isCrossRoomRegionFloatingMessage(SCFloatingMessage message) { bool _isCrossRoomRegionFloatingMessage(SCFloatingMessage message) {
return _isRegionRedPacketMessage(message) || return _isRegionRedPacketMessage(message) ||
_isRegionRoomRocketMessage(message) || _isRegionRoomRocketMessage(message) ||
_isRegionGameWinMessage(message); _isRegionGameWinMessage(message) ||
_isRegionCpGiftMessage(message) ||
_isRegionCpRelationMessage(message);
} }
bool _shouldDeferRegionRedPacketMessage( bool _shouldDeferRegionRedPacketMessage(
@ -931,15 +943,20 @@ class OverlayManager {
currentRoomId.isNotEmpty && currentRoomId.isNotEmpty &&
messageRoomId.isNotEmpty && messageRoomId.isNotEmpty &&
currentRoomId == messageRoomId; currentRoomId == messageRoomId;
final isGlobalStackedBroadcast =
message.type == 4 ||
message.type == 3 ||
message.type == 6 ||
_isRegionCpGiftMessage(message);
if (isCurrentVisibleRoom) { if (isCurrentVisibleRoom) {
// / RTM IM // //CP RTM IM
return message.type == 4 || message.type == 3 || message.type == 2; return isGlobalStackedBroadcast || message.type == 2;
} }
final isCurrentRoomMessage = final isCurrentRoomMessage =
currentRoomId.isNotEmpty && currentRoomId.isNotEmpty &&
messageRoomId.isNotEmpty && messageRoomId.isNotEmpty &&
currentRoomId == messageRoomId; currentRoomId == messageRoomId;
if ((message.type == 4 || message.type == 3 || message.type == 2) && if ((isGlobalStackedBroadcast || message.type == 2) &&
isCurrentRoomMessage) { isCurrentRoomMessage) {
return false; return false;
} }
@ -952,11 +969,10 @@ class OverlayManager {
} }
return SCGlobalConfig.isFloatingBroadcastAll && _homeRootTabsVisible; return SCGlobalConfig.isFloatingBroadcastAll && _homeRootTabsVisible;
} }
if ((message.type == 4 || message.type == 3) && if (isGlobalStackedBroadcast && rtcProvider.shouldShowRoomVisualEffects) {
rtcProvider.shouldShowRoomVisualEffects) {
return true; return true;
} }
if (message.type == 4 || message.type == 3) { if (isGlobalStackedBroadcast) {
return _homeRootTabsVisible; return _homeRootTabsVisible;
} }
if (SCGlobalConfig.isFloatingBroadcastRoomOnly) { if (SCGlobalConfig.isFloatingBroadcastRoomOnly) {

View File

@ -1524,25 +1524,12 @@ class SCAccountRepository implements SocialChatUserRepository {
String relationType = 'CP', String relationType = 'CP',
}) async { }) async {
final query = {"acceptUserId": acceptUserId, "relationType": relationType}; final query = {"acceptUserId": acceptUserId, "relationType": relationType};
_cpDebugLog('giftRelationCheck request query=$query'); return http.get<SCCpGiftRelationCheckRes>(
try { "/user/cp-relationship/gift-relation/check",
final result = await http.get<SCCpGiftRelationCheckRes>( queryParams: query,
"/user/cp-relationship/gift-relation/check", extra: const {BaseNetworkClient.silentErrorToastKey: true},
queryParams: query, fromJson: SCCpGiftRelationCheckRes.fromJson,
extra: const {BaseNetworkClient.silentErrorToastKey: true}, );
fromJson: SCCpGiftRelationCheckRes.fromJson,
);
_cpDebugLog(
'giftRelationCheck response query=$query '
'status=${result.status} msg=${result.msg} '
'userFull=${result.userRelationFull} '
'acceptFull=${result.acceptUserRelationFull}',
);
return result;
} catch (error) {
_cpDebugLog('giftRelationCheck failed query=$query error=$error');
rethrow;
}
} }
///user/cp/cabin ///user/cp/cabin

View File

@ -5,9 +5,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
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_room_effect_scheduler.dart';
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_widget.dart';
class SCRoomTopLayerGuard { class SCRoomTopLayerGuard {
static const int _maxDeferredActions = 16; static const int _maxDeferredActions = 16;
@ -56,11 +53,6 @@ class SCRoomTopLayerGuard {
} }
_activeListenable.value = true; _activeListenable.value = true;
OverlayManager().beginSuppressFloatingScreens(reason: reason); OverlayManager().beginSuppressFloatingScreens(reason: reason);
SCRoomEffectScheduler().beginSuppressEffects(reason: reason);
SCRoomEffectScheduler().clearDeferredTasks(reason: reason);
RoomEntranceHelper.clearQueue();
SCGiftVapSvgaManager().stopPlayback();
SCGiftVapSvgaManager().pauseAnim();
if (!keepDialogTags.contains(_redPacketOpenDialogTag)) { if (!keepDialogTags.contains(_redPacketOpenDialogTag)) {
unawaited(SmartDialog.dismiss(tag: _redPacketOpenDialogTag)); unawaited(SmartDialog.dismiss(tag: _redPacketOpenDialogTag));
} }
@ -81,8 +73,6 @@ class SCRoomTopLayerGuard {
return; return;
} }
OverlayManager().endSuppressFloatingScreens(reason: reason); OverlayManager().endSuppressFloatingScreens(reason: reason);
SCRoomEffectScheduler().endSuppressEffects(reason: reason);
SCGiftVapSvgaManager().resumeAnim();
_activeListenable.value = false; _activeListenable.value = false;
_drainDeferredActions(); _drainDeferredActions();
} }

View File

@ -37,7 +37,7 @@ class RoomCpProgressDialog extends StatelessWidget {
this.targetValue = 1, this.targetValue = 1,
this.expAwayText = '0', this.expAwayText = '0',
this.nextLevelText = 'Lv. 1', this.nextLevelText = 'Lv. 1',
this.cancelText = 'unbing', this.cancelText = 'unbind',
this.dismissTag = dialogTag, this.dismissTag = dialogTag,
this.onCancel, this.onCancel,
}); });
@ -68,7 +68,7 @@ class RoomCpProgressDialog extends StatelessWidget {
double targetValue = 1, double targetValue = 1,
String expAwayText = '0', String expAwayText = '0',
String nextLevelText = 'Lv. 1', String nextLevelText = 'Lv. 1',
String cancelText = 'unbing', String cancelText = 'unbind',
String dismissTag = dialogTag, String dismissTag = dialogTag,
VoidCallback? onCancel, VoidCallback? onCancel,
}) async { }) async {
@ -495,7 +495,7 @@ class _CancelButton extends StatelessWidget {
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
Text( Text(
text.trim().isEmpty ? 'unbing' : text.trim(), text.trim().isEmpty ? 'unbind' : text.trim(),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center, textAlign: TextAlign.center,

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart'; import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
@ -157,7 +159,7 @@ class _FloatingCpRelationScreenWidgetState
} }
final explicitText = widget.message.displayText?.trim() ?? ""; final explicitText = widget.message.displayText?.trim() ?? "";
if (explicitText.isNotEmpty) { if (explicitText.isNotEmpty) {
return _stripSystemNotificationPrefix(explicitText); return _stripSystemNotificationPrefix(_repairMojibake(explicitText));
} }
return _stripSystemNotificationPrefix( return _stripSystemNotificationPrefix(
scBuildCpRelationEstablishedNotice( scBuildCpRelationEstablishedNotice(
@ -169,7 +171,7 @@ class _FloatingCpRelationScreenWidgetState
} }
String _shortName(String? value) { String _shortName(String? value) {
final text = value?.trim() ?? ""; final text = _repairMojibake(value?.trim() ?? "");
if (text.length <= 14) { if (text.length <= 14) {
return text; return text;
} }
@ -188,7 +190,7 @@ class _FloatingCpRelationScreenWidgetState
} }
String _stripSystemNotificationPrefix(String text) { String _stripSystemNotificationPrefix(String text) {
return text return _repairMojibake(text)
.replaceFirst( .replaceFirst(
RegExp( RegExp(
r'^\s*[\[\【]\s*sys(?:tem|term)\s+notification\s*[\]\】]\s*', r'^\s*[\[\【]\s*sys(?:tem|term)\s+notification\s*[\]\】]\s*',
@ -199,6 +201,35 @@ class _FloatingCpRelationScreenWidgetState
.trim(); .trim();
} }
String _repairMojibake(String value) {
final text = value.trim();
if (text.isEmpty || !_looksLikeMojibake(text)) {
return text;
}
try {
final repaired = utf8.decode(latin1.encode(text), allowMalformed: true);
return _mojibakeScore(repaired) < _mojibakeScore(text) ? repaired : text;
} catch (_) {
return text;
}
}
bool _looksLikeMojibake(String value) {
return value.contains('Ã') ||
value.contains('Â') ||
value.contains('â') ||
value.contains('ð') ||
value.contains('<EFBFBD>');
}
int _mojibakeScore(String value) {
var score = 0;
for (final marker in const ['Ã', 'Â', 'â', 'ð', '<EFBFBD>']) {
score += marker.allMatches(value).length;
}
return score;
}
List<Color> _relationColors(String? relationType) { List<Color> _relationColors(String? relationType) {
switch (scNormalizeCpRelationType(relationType)) { switch (scNormalizeCpRelationType(relationType)) {
case "BROTHER": case "BROTHER":

View File

@ -612,16 +612,16 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
) { ) {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: 16.w, height: 20.w,
child: Center( child: Center(
child: SCUserBadgeStrip( child: SCUserBadgeStrip(
userId: userId, userId: userId,
fallbackBadges: _roomCardFallbackBadges(ref, profile), fallbackBadges: _roomCardFallbackBadges(ref, profile),
displayScope: SCUserBadgeDisplayScope.short, displayScope: SCUserBadgeDisplayScope.short,
height: 16.w, height: 20.w,
badgeHeight: 16.w, badgeHeight: 20.w,
longBadgeWidth: 32.w, longBadgeWidth: 44.w,
spacing: 4.w, spacing: 5.w,
maxBadges: 3, maxBadges: 3,
wrap: false, wrap: false,
reserveSpace: false, reserveSpace: false,
@ -1633,7 +1633,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
SocialChatUserProfile? profile, SocialChatUserProfile? profile,
) async { ) async {
final cp = _cpProfileData(profile); final cp = _cpProfileData(profile);
const cancelText = 'unbing'; const cancelText = 'unbind';
SCLoadingManager.show(); SCLoadingManager.show();
late final _RoomCpProgressData progress; late final _RoomCpProgressData progress;
try { try {
@ -1673,7 +1673,10 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
} }
Future<_RoomCpProgressData> _loadCpProgressData(_RoomProfileCpData cp) async { Future<_RoomCpProgressData> _loadCpProgressData(_RoomProfileCpData cp) async {
final partnerUserId = cp.rightUserId.trim(); final partnerUserId = _counterpartUserIdForCurrentUser(
leftUserId: cp.leftUserId,
rightUserId: cp.rightUserId,
);
final fallback = _RoomCpProgressData.fallback(cp); final fallback = _RoomCpProgressData.fallback(cp);
if (partnerUserId.isEmpty) { if (partnerUserId.isEmpty) {
return fallback; return fallback;
@ -1707,13 +1710,36 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
} }
} }
String _counterpartUserIdForCurrentUser({
required String leftUserId,
required String rightUserId,
}) {
final currentUserId =
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
final leftId = leftUserId.trim();
final rightId = rightUserId.trim();
if (currentUserId.isEmpty) {
return rightId;
}
if (currentUserId == leftId) {
return rightId;
}
if (currentUserId == rightId) {
return leftId;
}
return rightId;
}
Future<void> _showCpDismissConfirm( Future<void> _showCpDismissConfirm(
BuildContext context, BuildContext context,
_RoomProfileCpData cp, { _RoomProfileCpData cp, {
int? dismissCost, int? dismissCost,
}) async { }) async {
final l10n = SCAppLocalizations.of(context)!; final l10n = SCAppLocalizations.of(context)!;
final partnerUserId = cp.rightUserId.trim(); final partnerUserId = _counterpartUserIdForCurrentUser(
leftUserId: cp.leftUserId,
rightUserId: cp.rightUserId,
);
if (partnerUserId.isEmpty) { if (partnerUserId.isEmpty) {
return; return;
} }