修复飘窗bug
This commit is contained in:
parent
be6b2f8f88
commit
74e8162c1f
@ -11,6 +11,7 @@ import 'package:yumi/modules/user/settings/settings_route.dart';
|
||||
import 'package:yumi/modules/user/vip/vip_route.dart';
|
||||
import 'package:yumi/modules/wallet/wallet_route.dart';
|
||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_user_counter_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||
@ -117,11 +118,36 @@ class _MePage2State extends State<MePage2> {
|
||||
status.active == false || status.levelInt <= 0
|
||||
? '0'
|
||||
: status.levelInt.toString();
|
||||
if (profile.vipLevel == nextVipLevel) return;
|
||||
|
||||
profileManager.syncCurrentUserProfile(
|
||||
final nextProfile = socialChatProfileWithAvatarFrameResource(
|
||||
profile.copyWith(vipLevel: nextVipLevel),
|
||||
_avatarFramePropsFromVipStatus(status),
|
||||
expireTime: _expireTimeFromVipStatus(status),
|
||||
);
|
||||
profileManager.syncCurrentUserProfile(nextProfile);
|
||||
}
|
||||
|
||||
PropsResources? _avatarFramePropsFromVipStatus(SCVipStatusRes status) {
|
||||
if (!status.isActive) {
|
||||
return null;
|
||||
}
|
||||
final resource = status.avatarFrame;
|
||||
return socialChatAvatarFrameProps(
|
||||
id: resource?.resourceId,
|
||||
name: resource?.name,
|
||||
sourceUrl: resource?.sourceResourceUrl,
|
||||
cover: resource?.coverUrl ?? resource?.cover,
|
||||
code: "VIP_AVATAR_FRAME",
|
||||
);
|
||||
}
|
||||
|
||||
String? _expireTimeFromVipStatus(SCVipStatusRes status) {
|
||||
final expireAt = status.expireAt?.trim();
|
||||
if (expireAt == null || expireAt.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final normalized =
|
||||
expireAt.contains("T") ? expireAt : expireAt.replaceFirst(" ", "T");
|
||||
return DateTime.tryParse(normalized)?.millisecondsSinceEpoch.toString();
|
||||
}
|
||||
|
||||
void _refreshAfterVipReturn(SocialChatUserProfileManager profileManager) {
|
||||
@ -783,7 +809,9 @@ class _MePage2State extends State<MePage2> {
|
||||
),
|
||||
);
|
||||
} else if (kDebugMode) {
|
||||
debugPrint('[MePage2] hide Manager Center because yumiManager=$isYumiManager');
|
||||
debugPrint(
|
||||
'[MePage2] hide Manager Center because yumiManager=$isYumiManager',
|
||||
);
|
||||
}
|
||||
return _buildMenuCard(items);
|
||||
}
|
||||
|
||||
@ -297,6 +297,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
static const Duration _roomRocketGiftRefreshDelay = Duration(
|
||||
milliseconds: 700,
|
||||
);
|
||||
static const List<Duration> _roomRocketPostLaunchRefreshDelays = [
|
||||
Duration(milliseconds: 800),
|
||||
Duration(seconds: 2),
|
||||
Duration(seconds: 5),
|
||||
Duration(seconds: 10),
|
||||
];
|
||||
static const int _roomPasswordNotTrueErrorCode = 9005;
|
||||
|
||||
static const Duration _micListPollingInterval = Duration(seconds: 2);
|
||||
@ -337,6 +343,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
Timer? _roomEntryEffectTimer;
|
||||
Timer? _roomRocketLaunchAnimationTimer;
|
||||
Timer? _roomRocketGiftRefreshTimer;
|
||||
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
||||
String? _lastRoomRocketLaunchNoticeKey;
|
||||
Timer? _roomRedPacketPresenceTimer;
|
||||
Future<void>? _rtcEnginePrewarmTask;
|
||||
@ -485,6 +492,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_roomRocketLaunchAnimationTimer = null;
|
||||
_roomRocketGiftRefreshTimer?.cancel();
|
||||
_roomRocketGiftRefreshTimer = null;
|
||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||
}
|
||||
_roomVisualEffectsEnabled = enabled;
|
||||
notifyListeners();
|
||||
@ -2112,14 +2120,22 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final existingVipLevel = _positiveProfileVipLevelHint(currentProfile);
|
||||
final vipLevel =
|
||||
existingVipLevel ?? await _loadCurrentVipLevelForColoredId();
|
||||
final needsVipStatus =
|
||||
existingVipLevel == null || currentProfile.getHeaddress() == null;
|
||||
final vipStatus = needsVipStatus ? await _loadCurrentVipStatus() : null;
|
||||
final vipLevel = existingVipLevel ?? _vipLevelFromStatus(vipStatus);
|
||||
if (vipLevel == null || vipLevel.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final updatedProfile = currentProfile.copyWith(vipLevel: vipLevel);
|
||||
if (!_profileHasPositiveVipLevelHint(currentProfile)) {
|
||||
final updatedProfile = socialChatProfileWithAvatarFrameResource(
|
||||
currentProfile.copyWith(vipLevel: vipLevel),
|
||||
_avatarFramePropsFromVipStatus(vipStatus),
|
||||
expireTime: _expireTimeFromVipStatus(vipStatus),
|
||||
);
|
||||
if (!_profileHasPositiveVipLevelHint(currentProfile) ||
|
||||
currentProfile.getHeaddress()?.sourceUrl !=
|
||||
updatedProfile.getHeaddress()?.sourceUrl) {
|
||||
AccountStorage().setCurrentUser(
|
||||
currentUser.copyWith(userProfile: updatedProfile),
|
||||
);
|
||||
@ -2162,18 +2178,18 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _loadCurrentVipLevelForColoredId() async {
|
||||
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
||||
final repository = SCVipRepositoryImp();
|
||||
try {
|
||||
final home = await repository.vipHome();
|
||||
final homeVipLevel = _vipLevelFromStatus(home.state);
|
||||
if (homeVipLevel != null) {
|
||||
return homeVipLevel;
|
||||
final state = home.state;
|
||||
if (state != null && state.levelInt > 0) {
|
||||
return state;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
return _vipLevelFromStatus(await repository.vipStatus());
|
||||
return repository.vipStatus();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
@ -2190,6 +2206,30 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
return level > 0 ? level.toString() : '0';
|
||||
}
|
||||
|
||||
PropsResources? _avatarFramePropsFromVipStatus(SCVipStatusRes? status) {
|
||||
if (status?.isActive != true) {
|
||||
return null;
|
||||
}
|
||||
final resource = status?.avatarFrame;
|
||||
return socialChatAvatarFrameProps(
|
||||
id: resource?.resourceId,
|
||||
name: resource?.name,
|
||||
sourceUrl: resource?.sourceResourceUrl,
|
||||
cover: resource?.coverUrl ?? resource?.cover,
|
||||
code: "VIP_AVATAR_FRAME",
|
||||
);
|
||||
}
|
||||
|
||||
String? _expireTimeFromVipStatus(SCVipStatusRes? status) {
|
||||
final expireAt = status?.expireAt?.trim();
|
||||
if (expireAt == null || expireAt.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final normalized =
|
||||
expireAt.contains("T") ? expireAt : expireAt.replaceFirst(" ", "T");
|
||||
return DateTime.tryParse(normalized)?.millisecondsSinceEpoch.toString();
|
||||
}
|
||||
|
||||
JoinRoomRes? _mergeKnownVipIntoCurrentRoom(
|
||||
JoinRoomRes? room,
|
||||
SocialChatUserProfile currentProfile,
|
||||
@ -2764,6 +2804,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
userProfileResolver: roomRocketRewardUserProfileForRecord,
|
||||
onClose: () {
|
||||
SmartDialog.dismiss(tag: _roomRocketRewardDialogTag);
|
||||
scheduleRoomRocketPostLaunchStatusRefresh(roomId: roomId);
|
||||
if (recordIds.isNotEmpty) {
|
||||
unawaited(
|
||||
SCChatRoomRepository().ackRoomRocketRewardPopups(
|
||||
@ -2817,6 +2858,32 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void scheduleRoomRocketPostLaunchStatusRefresh({String? roomId}) {
|
||||
final resolvedRoomId =
|
||||
(roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||
if (resolvedRoomId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||
for (final delay in _roomRocketPostLaunchRefreshDelays) {
|
||||
final timer = Timer(delay, () {
|
||||
if (resolvedRoomId !=
|
||||
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) {
|
||||
return;
|
||||
}
|
||||
unawaited(refreshRoomRocketStatus(roomId: resolvedRoomId));
|
||||
});
|
||||
_roomRocketPostLaunchRefreshTimers.add(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void _cancelRoomRocketPostLaunchStatusRefresh() {
|
||||
for (final timer in _roomRocketPostLaunchRefreshTimers) {
|
||||
timer.cancel();
|
||||
}
|
||||
_roomRocketPostLaunchRefreshTimers.clear();
|
||||
}
|
||||
|
||||
void handleRoomRocketLaunchBroadcast(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
@ -2845,11 +2912,13 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_roomRocketLaunchAnimationTimer = null;
|
||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
||||
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
||||
scheduleRoomRocketPostLaunchStatusRefresh(roomId: launch.roomId);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
||||
scheduleRoomRocketPostLaunchStatusRefresh(roomId: launch.roomId);
|
||||
}
|
||||
|
||||
String? _rocketLaunchAnimationUrlForLevel(int level) {
|
||||
@ -2992,6 +3061,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_roomRocketLaunchAnimationTimer?.cancel();
|
||||
_roomRocketLaunchAnimationTimer = null;
|
||||
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
||||
scheduleRoomRocketPostLaunchStatusRefresh(
|
||||
roomId: launch.roomId,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -3572,6 +3644,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_roomRocketLaunchAnimationTimer = null;
|
||||
_roomRocketGiftRefreshTimer?.cancel();
|
||||
_roomRocketGiftRefreshTimer = null;
|
||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||
_lastRoomRocketLaunchNoticeKey = null;
|
||||
_stopRoomRedPacketPresenceHeartbeat();
|
||||
_previewRoomSeatCount = null;
|
||||
|
||||
@ -1946,7 +1946,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
unawaited(rtcProvider.refreshRoomRocketStatus(roomId: roomId));
|
||||
}
|
||||
|
||||
void _handleVoiceRoomRocketLaunchBroadcast(dynamic payload) {
|
||||
void _handleVoiceRoomRocketLaunchBroadcast(
|
||||
dynamic payload, {
|
||||
bool isRegionBroadcast = false,
|
||||
}) {
|
||||
final launch = RoomRocketLaunchBroadcastMessage.fromJson(
|
||||
_broadcastPayloadMap(payload),
|
||||
);
|
||||
@ -1978,10 +1981,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
if (isCurrentRoom && !isCurrentVisibleRoom) {
|
||||
if (isCurrentRoom && !isCurrentVisibleRoom && !isRegionBroadcast) {
|
||||
return;
|
||||
}
|
||||
OverlayManager().addMessage(_buildRoomRocketLaunchFloatingMessage(launch));
|
||||
OverlayManager().addMessage(
|
||||
_buildRoomRocketLaunchFloatingMessage(
|
||||
launch,
|
||||
isRegionBroadcast: isRegionBroadcast,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleVoiceRoomRocketRewardPopup(dynamic payload) {
|
||||
@ -2095,6 +2103,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
userProfileResolver: userProfileResolver,
|
||||
onClose: () {
|
||||
SmartDialog.dismiss(tag: _roomRocketRewardDialogTag);
|
||||
_scheduleRoomRocketPostLaunchStatusRefresh(roomId);
|
||||
if (recordIds.isNotEmpty) {
|
||||
unawaited(
|
||||
SCChatRoomRepository().ackRoomRocketRewardPopups(recordIds),
|
||||
@ -2154,6 +2163,19 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void _scheduleRoomRocketPostLaunchStatusRefresh(String roomId) {
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
).scheduleRoomRocketPostLaunchStatusRefresh(roomId: roomId);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
List<SCRoomRocketRewardRecordRes> _roomRocketRewardRecordsFromPayload(
|
||||
Map<String, dynamic> payload,
|
||||
) {
|
||||
@ -2183,8 +2205,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
SCFloatingMessage _buildRoomRocketLaunchFloatingMessage(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
RoomRocketLaunchBroadcastMessage launch, {
|
||||
bool isRegionBroadcast = false,
|
||||
}) {
|
||||
return SCFloatingMessage(
|
||||
type: 3,
|
||||
roomId: launch.roomId,
|
||||
@ -2203,6 +2226,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
fallbackUrl: launch.rocketIconUrl,
|
||||
giftId: launch.launchNo,
|
||||
durationSeconds: launch.durationSeconds,
|
||||
broadcastScope:
|
||||
isRegionBroadcast ? SCFloatingMessage.broadcastScopeRegion : '',
|
||||
priority: 1000,
|
||||
);
|
||||
}
|
||||
@ -2637,7 +2662,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
var fdata = data["data"];
|
||||
final payload = _broadcastPayloadMap(fdata);
|
||||
final launchRoomId = _payloadRoomId(payload);
|
||||
if (_isCurrentHiddenVoiceRoom(launchRoomId)) {
|
||||
if (!isRegionBroadcastGroup &&
|
||||
_isCurrentHiddenVoiceRoom(launchRoomId)) {
|
||||
return;
|
||||
}
|
||||
final rocketIconUrl = _firstNonBlank([
|
||||
@ -2666,11 +2692,18 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
userId: _payloadText(payload["actualAccount"]),
|
||||
giftUrl: rocketIconUrl,
|
||||
fallbackUrl: rocketIconUrl,
|
||||
broadcastScope:
|
||||
isRegionBroadcastGroup
|
||||
? SCFloatingMessage.broadcastScopeRegion
|
||||
: '',
|
||||
priority: 1000,
|
||||
);
|
||||
OverlayManager().addMessage(msg);
|
||||
} else if (type == SCRoomMsgType.voiceRoomRocketLaunchBroadcast) {
|
||||
_handleVoiceRoomRocketLaunchBroadcast(data["data"]);
|
||||
_handleVoiceRoomRocketLaunchBroadcast(
|
||||
data["data"],
|
||||
isRegionBroadcast: isRegionBroadcastGroup,
|
||||
);
|
||||
} else if (type == SCRoomMsgType.voiceRoomRocketStatusUpdate) {
|
||||
_handleVoiceRoomRocketStatusUpdate(data["data"]);
|
||||
} else if (type == SCRoomMsgType.voiceRoomRocketRewardPopup) {
|
||||
|
||||
@ -165,20 +165,31 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final profileVipLevel = profile.vipLevel?.trim();
|
||||
if (profileVipLevel != null && profileVipLevel.isNotEmpty) {
|
||||
if (profileVipLevel != null &&
|
||||
profileVipLevel.isNotEmpty &&
|
||||
profile.getHeaddress() != null) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
final vipStatus = await _loadCurrentVipStatus();
|
||||
final currentVipLevel = currentUserProfile?.vipLevel?.trim();
|
||||
final resolvedVipLevel =
|
||||
currentVipLevel != null && currentVipLevel.isNotEmpty
|
||||
profileVipLevel != null && profileVipLevel.isNotEmpty
|
||||
? profileVipLevel
|
||||
: currentVipLevel != null && currentVipLevel.isNotEmpty
|
||||
? currentVipLevel
|
||||
: await _loadCurrentVipLevel();
|
||||
if (resolvedVipLevel == null || resolvedVipLevel.isEmpty) {
|
||||
return profile;
|
||||
: _vipLevelFromStatus(vipStatus);
|
||||
|
||||
var nextProfile = profile;
|
||||
if (resolvedVipLevel != null && resolvedVipLevel.isNotEmpty) {
|
||||
nextProfile = nextProfile.copyWith(vipLevel: resolvedVipLevel);
|
||||
}
|
||||
|
||||
return profile.copyWith(vipLevel: resolvedVipLevel);
|
||||
return socialChatProfileWithAvatarFrameResource(
|
||||
nextProfile,
|
||||
_avatarFramePropsFromVipStatus(vipStatus),
|
||||
expireTime: _expireTimeFromVipStatus(vipStatus),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isCurrentUserId(String userId) {
|
||||
@ -188,23 +199,47 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
||||
currentUserId == userId;
|
||||
}
|
||||
|
||||
Future<String?> _loadCurrentVipLevel() async {
|
||||
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
||||
final repository = SCVipRepositoryImp();
|
||||
try {
|
||||
final home = await repository.vipHome();
|
||||
final homeVipLevel = _vipLevelFromStatus(home.state);
|
||||
if (homeVipLevel != null) {
|
||||
return homeVipLevel;
|
||||
final state = home.state;
|
||||
if (state != null && state.levelInt > 0) {
|
||||
return state;
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
return _vipLevelFromStatus(await repository.vipStatus());
|
||||
return repository.vipStatus();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
PropsResources? _avatarFramePropsFromVipStatus(SCVipStatusRes? status) {
|
||||
if (status?.isActive != true) {
|
||||
return null;
|
||||
}
|
||||
final resource = status?.avatarFrame;
|
||||
return socialChatAvatarFrameProps(
|
||||
id: resource?.resourceId,
|
||||
name: resource?.name,
|
||||
sourceUrl: resource?.sourceResourceUrl,
|
||||
cover: resource?.coverUrl ?? resource?.cover,
|
||||
code: "VIP_AVATAR_FRAME",
|
||||
);
|
||||
}
|
||||
|
||||
String? _expireTimeFromVipStatus(SCVipStatusRes? status) {
|
||||
final expireAt = status?.expireAt?.trim();
|
||||
if (expireAt == null || expireAt.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final normalized =
|
||||
expireAt.contains("T") ? expireAt : expireAt.replaceFirst(" ", "T");
|
||||
return DateTime.tryParse(normalized)?.millisecondsSinceEpoch.toString();
|
||||
}
|
||||
|
||||
String? _vipLevelFromStatus(SCVipStatusRes? status) {
|
||||
if (status == null) {
|
||||
return null;
|
||||
|
||||
@ -492,8 +492,9 @@ class SocialChatUserProfile {
|
||||
PropsResources? getHeaddress() {
|
||||
PropsResources? pr;
|
||||
_useProps?.forEach((value) {
|
||||
if (value.propsResources?.type == SCPropsType.AVATAR_FRAME.name) {
|
||||
pr = value.propsResources;
|
||||
final resource = value.propsResources;
|
||||
if (socialChatIsAvatarFrameResource(resource)) {
|
||||
pr = resource;
|
||||
}
|
||||
});
|
||||
return pr;
|
||||
@ -1249,6 +1250,9 @@ class PropsResources {
|
||||
}
|
||||
|
||||
PropsResources.fromJson(dynamic json) {
|
||||
if (json is! Map) {
|
||||
return;
|
||||
}
|
||||
_amount = json['amount'];
|
||||
_code = json['code'];
|
||||
_cover = json['cover'] ?? json['coverUrl'];
|
||||
@ -1264,9 +1268,12 @@ class PropsResources {
|
||||
_roomNotOpenedUrl = json['roomNotOpenedUrl'];
|
||||
_roomOpenedUrl = json['roomOpenedUrl'];
|
||||
_roomSendCoverUrl = json['roomSendCoverUrl'];
|
||||
_sourceUrl = json['sourceUrl'] ?? json['resourceUrl'] ?? json['url'];
|
||||
_sysOrigin = json['sysOrigin'];
|
||||
_type = json['type'];
|
||||
_sourceUrl =
|
||||
_isAvatarFrameType(_type)
|
||||
? json['sourceUrl']
|
||||
: json['sourceUrl'] ?? json['resourceUrl'] ?? json['url'];
|
||||
_updateTime = json['updateTime'];
|
||||
}
|
||||
|
||||
@ -1395,6 +1402,90 @@ class PropsResources {
|
||||
}
|
||||
}
|
||||
|
||||
bool socialChatIsAvatarFrameResource(PropsResources? resource) {
|
||||
if (!_isAvatarFrameType(resource?.type)) {
|
||||
return false;
|
||||
}
|
||||
final sourceUrl = resource?.sourceUrl?.trim() ?? "";
|
||||
if (sourceUrl.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return !_looksLikeEntryEffectResource(sourceUrl) &&
|
||||
!_looksLikeEntryEffectResource(resource?.cover ?? "");
|
||||
}
|
||||
|
||||
PropsResources? socialChatAvatarFrameProps({
|
||||
String? id,
|
||||
String? name,
|
||||
String? sourceUrl,
|
||||
String? cover,
|
||||
String? code,
|
||||
}) {
|
||||
final resource = PropsResources(
|
||||
id: id,
|
||||
name: name,
|
||||
code: code,
|
||||
sourceUrl: sourceUrl,
|
||||
cover: cover,
|
||||
type: SCPropsType.AVATAR_FRAME.name,
|
||||
);
|
||||
return socialChatIsAvatarFrameResource(resource) ? resource : null;
|
||||
}
|
||||
|
||||
SocialChatUserProfile socialChatProfileWithAvatarFrameResource(
|
||||
SocialChatUserProfile profile,
|
||||
PropsResources? avatarFrame, {
|
||||
String? expireTime,
|
||||
}) {
|
||||
if (!socialChatIsAvatarFrameResource(avatarFrame)) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
final currentHeaddress = profile.getHeaddress();
|
||||
if (currentHeaddress != null) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
final useProps = <UseProps>[
|
||||
...?profile.useProps?.where(
|
||||
(item) => !_isAvatarFrameType(item.propsResources?.type),
|
||||
),
|
||||
UseProps(
|
||||
expireTime: expireTime ?? "4102444800000",
|
||||
propsResources: avatarFrame,
|
||||
userId: profile.id,
|
||||
),
|
||||
];
|
||||
|
||||
return profile.copyWith(useProps: useProps);
|
||||
}
|
||||
|
||||
bool _isAvatarFrameType(String? type) {
|
||||
return type?.trim().toUpperCase() == SCPropsType.AVATAR_FRAME.name;
|
||||
}
|
||||
|
||||
bool _looksLikeEntryEffectResource(String value) {
|
||||
if (value.trim().isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final text = _decodeResourceText(value).toLowerCase();
|
||||
final entryResourcePattern = RegExp(
|
||||
r'(^|[\/_\-.?=&])(?:entry|entrance|ride|mountain|mountains)([\/_\-.?=&]|$)',
|
||||
);
|
||||
return entryResourcePattern.hasMatch(text) ||
|
||||
text.contains('entryeffect') ||
|
||||
text.contains('进场') ||
|
||||
text.contains('坐骑');
|
||||
}
|
||||
|
||||
String _decodeResourceText(String value) {
|
||||
try {
|
||||
return Uri.decodeFull(value);
|
||||
} catch (_) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// account : ""
|
||||
/// expiredTime : 0
|
||||
|
||||
|
||||
@ -87,19 +87,24 @@ class SCRoomRocketStatusRes {
|
||||
}
|
||||
|
||||
SCRoomRocketStatusRes.fromJson(dynamic json) {
|
||||
_roomId = json['roomId'];
|
||||
_date = json['date'] ?? json['dayKey'];
|
||||
_roundNo = _intValue(json['roundNo']);
|
||||
_displayRound = _boolValue(json['displayRound']);
|
||||
_currentLevel = _intValue(json['currentLevel']);
|
||||
_needEnergy = _intValue(json['needEnergy']);
|
||||
_displayPercent = _numValue(json['displayPercent']);
|
||||
_roomId = json['roomId'] ?? json['room_id'];
|
||||
_date = json['date'] ?? json['dayKey'] ?? json['day_key'];
|
||||
_roundNo = _intValue(json['roundNo'] ?? json['round_no']);
|
||||
_displayRound = _boolValue(json['displayRound'] ?? json['display_round']);
|
||||
_currentLevel = _intValue(json['currentLevel'] ?? json['current_level']);
|
||||
_needEnergy = _intValue(json['needEnergy'] ?? json['need_energy']);
|
||||
_displayPercent = _numValue(
|
||||
json['displayPercent'] ?? json['display_percent'],
|
||||
);
|
||||
_shake = _boolValue(json['shake']);
|
||||
_level = json['level'] ?? _currentLevel;
|
||||
_levelName = json['levelName'];
|
||||
_currentEnergy = _intValue(json['currentEnergy']);
|
||||
_maxEnergy = _intValue(json['maxEnergy']) ?? _needEnergy;
|
||||
_energyPercent = _numValue(json['energyPercent']) ?? _displayPercent;
|
||||
_level = json['level'] ?? json['rocket_level'] ?? _currentLevel;
|
||||
_levelName = json['levelName'] ?? json['level_name'];
|
||||
_currentEnergy = _intValue(json['currentEnergy'] ?? json['current_energy']);
|
||||
_maxEnergy =
|
||||
_intValue(json['maxEnergy'] ?? json['max_energy']) ?? _needEnergy;
|
||||
_energyPercent =
|
||||
_numValue(json['energyPercent'] ?? json['energy_percent']) ??
|
||||
_displayPercent;
|
||||
_status = json['status'];
|
||||
_statusDesc = json['statusDesc'];
|
||||
_levels = _levelList(json['levels']);
|
||||
@ -124,6 +129,8 @@ class SCRoomRocketStatusRes {
|
||||
_firstPayload([
|
||||
json['rocketKings'],
|
||||
json['rocket_kings'],
|
||||
json['rocketKingUsers'],
|
||||
json['rocket_king_users'],
|
||||
json['kings'],
|
||||
json['podium'],
|
||||
]),
|
||||
@ -146,18 +153,28 @@ class SCRoomRocketStatusRes {
|
||||
final resetRemainingMillis = _intValue(
|
||||
json['resetRemainingMillis'] ??
|
||||
json['resetRemainMillis'] ??
|
||||
json['reset_remaining_millis'] ??
|
||||
json['reset_remain_millis'] ??
|
||||
json['remainingMillis'] ??
|
||||
json['remaining_millis'] ??
|
||||
json['remainMillis'],
|
||||
);
|
||||
_resetRemainingSeconds =
|
||||
_intValue(
|
||||
json['resetRemainingSeconds'] ??
|
||||
json['resetRemainSeconds'] ??
|
||||
json['reset_remaining_seconds'] ??
|
||||
json['reset_remain_seconds'] ??
|
||||
json['remainingSeconds'] ??
|
||||
json['remaining_seconds'] ??
|
||||
json['remainSeconds'] ??
|
||||
json['remain_seconds'] ??
|
||||
json['countdownSeconds'] ??
|
||||
json['countdown_seconds'] ??
|
||||
json['resetCountdownSeconds'] ??
|
||||
json['reset_countdown_seconds'] ??
|
||||
json['secondsToReset'] ??
|
||||
json['seconds_to_reset'] ??
|
||||
json['resetSeconds'],
|
||||
) ??
|
||||
(resetRemainingMillis == null
|
||||
|
||||
@ -712,11 +712,20 @@ class OverlayManager {
|
||||
return message.type == 4 && message.isRegionBroadcast;
|
||||
}
|
||||
|
||||
bool _isRegionRoomRocketMessage(SCFloatingMessage message) {
|
||||
return message.type == 3 && message.isRegionBroadcast;
|
||||
}
|
||||
|
||||
bool _isCrossRoomRegionFloatingMessage(SCFloatingMessage message) {
|
||||
return _isRegionRedPacketMessage(message) ||
|
||||
_isRegionRoomRocketMessage(message);
|
||||
}
|
||||
|
||||
bool _shouldDeferRegionRedPacketMessage(
|
||||
BuildContext context,
|
||||
SCFloatingMessage message,
|
||||
) {
|
||||
if (!_isRegionRedPacketMessage(message) ||
|
||||
if (!_isCrossRoomRegionFloatingMessage(message) ||
|
||||
SCGlobalConfig.isFloatingBroadcastClosed) {
|
||||
return false;
|
||||
}
|
||||
@ -828,20 +837,21 @@ class OverlayManager {
|
||||
messageRoomId.isNotEmpty &&
|
||||
currentRoomId == messageRoomId;
|
||||
if (isCurrentVisibleRoom) {
|
||||
// 区域红包在当前房间仍然需要展示;区域礼物当前房间由 RTM 层跳过,避免和房间 IM 礼物飘屏重复。
|
||||
return message.type == 4;
|
||||
// 区域红包/火箭在当前房间仍然需要展示;区域礼物当前房间由 RTM 层跳过,避免和房间 IM 礼物飘屏重复。
|
||||
return message.type == 4 || message.type == 3;
|
||||
}
|
||||
final isCurrentRoomMessage =
|
||||
currentRoomId.isNotEmpty &&
|
||||
messageRoomId.isNotEmpty &&
|
||||
currentRoomId == messageRoomId;
|
||||
if (message.type == 4 && isCurrentRoomMessage) {
|
||||
if ((message.type == 4 || message.type == 3) && isCurrentRoomMessage) {
|
||||
return false;
|
||||
}
|
||||
if (message.type == 4 && rtcProvider.shouldShowRoomVisualEffects) {
|
||||
if ((message.type == 4 || message.type == 3) &&
|
||||
rtcProvider.shouldShowRoomVisualEffects) {
|
||||
return true;
|
||||
}
|
||||
if (message.type == 4) {
|
||||
if (message.type == 4 || message.type == 3) {
|
||||
return _homeRootTabsVisible;
|
||||
}
|
||||
if (SCGlobalConfig.isFloatingBroadcastRoomOnly) {
|
||||
@ -860,7 +870,8 @@ class OverlayManager {
|
||||
|
||||
while (_messageQueue.isNotEmpty) {
|
||||
final message = _messageQueue.removeFirst();
|
||||
if (!message.isRegionBroadcast || _isRegionRedPacketMessage(message)) {
|
||||
if (!message.isRegionBroadcast ||
|
||||
_isCrossRoomRegionFloatingMessage(message)) {
|
||||
newQueue.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ class AccountStorage {
|
||||
PropsResources? getHeaddress() {
|
||||
PropsResources? pr;
|
||||
_currentUser?.userProfile?.useProps?.forEach((value) {
|
||||
if (value.propsResources?.type == SCPropsType.AVATAR_FRAME.name) {
|
||||
if (socialChatIsAvatarFrameResource(value.propsResources)) {
|
||||
///判断有没有过期
|
||||
if (int.parse(value.expireTime ?? "0") >
|
||||
DateTime.now().millisecondsSinceEpoch) {
|
||||
|
||||
@ -198,17 +198,10 @@ class _RoomRocketEntryButtonState extends State<_RoomRocketEntryButton>
|
||||
SizedBox(
|
||||
width: 42.w,
|
||||
height: 8.w,
|
||||
child:
|
||||
widget.progressUrl.trim().isEmpty
|
||||
? _RocketEntryProgressBar(percent: widget.progressPercent)
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(5.w),
|
||||
child: _RocketEntryImage(
|
||||
source: widget.progressUrl,
|
||||
loading: false,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: _RocketEntryProgressBar(
|
||||
percent: widget.progressPercent,
|
||||
imageUrl: widget.progressUrl,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -218,13 +211,29 @@ class _RoomRocketEntryButtonState extends State<_RoomRocketEntryButton>
|
||||
}
|
||||
|
||||
class _RocketEntryProgressBar extends StatelessWidget {
|
||||
const _RocketEntryProgressBar({required this.percent});
|
||||
const _RocketEntryProgressBar({required this.percent, this.imageUrl = ''});
|
||||
|
||||
final double percent;
|
||||
final String imageUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final value = (percent / 100).clamp(0.0, 1.0).toDouble();
|
||||
final progressImageUrl = imageUrl.trim();
|
||||
final fill =
|
||||
progressImageUrl.isEmpty
|
||||
? const DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFFB8FFE6), Color(0xFF1BD091)],
|
||||
),
|
||||
),
|
||||
)
|
||||
: _RocketEntryImage(
|
||||
source: progressImageUrl,
|
||||
loading: false,
|
||||
fit: BoxFit.fill,
|
||||
);
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.w),
|
||||
@ -241,13 +250,7 @@ class _RocketEntryProgressBar extends StatelessWidget {
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: value,
|
||||
heightFactor: 1,
|
||||
child: const DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFFB8FFE6), Color(0xFF1BD091)],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: fill,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -90,6 +90,38 @@ void main() {
|
||||
expect(status.rocketKingsForLevel(2), hasLength(3));
|
||||
});
|
||||
|
||||
test('parses refreshed rocket status from snake case payloads', () {
|
||||
final status = SCRoomRocketStatusRes.fromJson({
|
||||
'room_id': 'room-1',
|
||||
'current_level': 2,
|
||||
'display_percent': 0,
|
||||
'current_energy': 0,
|
||||
'need_energy': 200,
|
||||
'round_no': 4,
|
||||
'levels': [
|
||||
{
|
||||
'level': 1,
|
||||
'rocket_icon_url': 'icon-1',
|
||||
'progress_bar_url': 'progress-1',
|
||||
},
|
||||
{
|
||||
'level': 2,
|
||||
'rocket_icon_url': 'icon-2',
|
||||
'progress_bar_url': 'progress-2',
|
||||
},
|
||||
],
|
||||
});
|
||||
final levels = RoomRocketApiMapper.levelsFromStatus(status);
|
||||
|
||||
expect(status.roomId, 'room-1');
|
||||
expect(status.currentLevel, 2);
|
||||
expect(status.displayPercent, 0);
|
||||
expect(status.needEnergy, 200);
|
||||
expect(status.roundNo, 4);
|
||||
expect(levels?.last.imageUrl, 'icon-2');
|
||||
expect(levels?.last.progressImageUrl, 'progress-2');
|
||||
});
|
||||
|
||||
test(
|
||||
'uses top-level reward preview for the response level instead of current level',
|
||||
() {
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:yumi/shared/business_logic/models/res/room_gift_rank_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/room_member_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/room_user_card_res.dart';
|
||||
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
||||
|
||||
void main() {
|
||||
group('VIP colored ID', () {
|
||||
@ -181,5 +182,65 @@ void main() {
|
||||
expect(mic.user?.vipLevelForColoredId, 5);
|
||||
expect(mic.user?.shouldShowColoredSpecialIdText(), isTrue);
|
||||
});
|
||||
|
||||
test('keeps avatar frame separate from entry effect resources', () {
|
||||
final profile = SocialChatUserProfile.fromJson({
|
||||
'account': '10001',
|
||||
'useProps': [
|
||||
{
|
||||
'propsResources': {
|
||||
'type': 'AVATAR_FRAME',
|
||||
'url': 'https://example.test/entry/effect.svga',
|
||||
},
|
||||
},
|
||||
{
|
||||
'propsResources': {
|
||||
'type': 'RIDE',
|
||||
'sourceUrl': 'https://example.test/ride/effect.svga',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(profile.getHeaddress(), isNull);
|
||||
});
|
||||
|
||||
test('accepts avatar frame from explicit sourceUrl only', () {
|
||||
final profile = SocialChatUserProfile.fromJson({
|
||||
'account': '10001',
|
||||
'useProps': [
|
||||
{
|
||||
'propsResources': {
|
||||
'type': 'AVATAR_FRAME',
|
||||
'sourceUrl': 'https://example.test/avatar/frame.svga',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(profile.getHeaddress()?.sourceUrl, contains('avatar/frame.svga'));
|
||||
});
|
||||
|
||||
test('merges active VIP avatar frame into profile props', () {
|
||||
final profile = SocialChatUserProfile.fromJson({'id': '10001'});
|
||||
final vipFrame = SCVipResourceRes.fromJson({
|
||||
'resourceId': 'vip-frame-1',
|
||||
'url': 'https://example.test/vip/avatar_frame.svga',
|
||||
'coverUrl': 'https://example.test/vip/avatar_frame.png',
|
||||
});
|
||||
|
||||
final merged = socialChatProfileWithAvatarFrameResource(
|
||||
profile,
|
||||
socialChatAvatarFrameProps(
|
||||
id: vipFrame.resourceId,
|
||||
sourceUrl: vipFrame.sourceResourceUrl,
|
||||
cover: vipFrame.coverUrl,
|
||||
code: 'VIP_AVATAR_FRAME',
|
||||
),
|
||||
);
|
||||
|
||||
expect(merged.getHeaddress()?.sourceUrl, contains('avatar_frame.svga'));
|
||||
expect(merged.getHeaddress()?.cover, contains('avatar_frame.png'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user