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