bug fix
This commit is contained in:
parent
f8e13f31ba
commit
6eb7cb91df
@ -1,4 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:carousel_slider/carousel_slider.dart';
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -9,6 +10,7 @@ import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
|||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/shared/tools/sc_user_utils.dart';
|
import 'package:yumi/shared/tools/sc_user_utils.dart';
|
||||||
import 'package:yumi/modules/index/main_route.dart';
|
import 'package:yumi/modules/index/main_route.dart';
|
||||||
|
import 'package:yumi/main.dart' show routeObserver;
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
@ -61,7 +63,7 @@ class PersonDetailPage extends StatefulWidget {
|
|||||||
_PersonDetailPageState createState() => _PersonDetailPageState();
|
_PersonDetailPageState createState() => _PersonDetailPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PersonDetailPageState extends State<PersonDetailPage> {
|
class _PersonDetailPageState extends State<PersonDetailPage> with RouteAware {
|
||||||
static const Color _profileBg = Color(0xff072121);
|
static const Color _profileBg = Color(0xff072121);
|
||||||
static const Color _cardBg = Color(0xff08251E);
|
static const Color _cardBg = Color(0xff08251E);
|
||||||
static const Color _profileBorder = Color(0xffB2FBCC);
|
static const Color _profileBorder = Color(0xffB2FBCC);
|
||||||
@ -86,6 +88,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
// 添加滚动控制器
|
// 添加滚动控制器
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
double _opacity = 0.0;
|
double _opacity = 0.0;
|
||||||
|
PageRoute<dynamic>? _routeObserverRoute;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -155,6 +158,52 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
_loadGiftWall();
|
_loadGiftWall();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
final route = ModalRoute.of(context);
|
||||||
|
if (route is PageRoute<dynamic> && _routeObserverRoute != route) {
|
||||||
|
if (_routeObserverRoute != null) {
|
||||||
|
routeObserver.unsubscribe(this);
|
||||||
|
}
|
||||||
|
_routeObserverRoute = route;
|
||||||
|
routeObserver.subscribe(this, route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didPopNext() {
|
||||||
|
_refreshProfileRelationsAfterResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshProfileRelationsAfterResume() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ref = Provider.of<SocialChatUserProfileManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
|
final profile = ref.userProfile;
|
||||||
|
final loadedUserId = profile?.id?.trim() ?? "";
|
||||||
|
final targetUserId = widget.tageId.trim();
|
||||||
|
if (targetUserId.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final hasRelationCards =
|
||||||
|
(profile?.cpList?.isNotEmpty ?? false) ||
|
||||||
|
(profile?.closeFriendList?.isNotEmpty ?? false);
|
||||||
|
if (loadedUserId.isEmpty ||
|
||||||
|
loadedUserId != targetUserId ||
|
||||||
|
!hasRelationCards) {
|
||||||
|
unawaited(ref.reloadUserInfoById(targetUserId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unawaited(ref.refreshLoadedCpProfiles());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void userCounter(String userId) async {
|
void userCounter(String userId) async {
|
||||||
try {
|
try {
|
||||||
var userCounterList = await SCAccountRepository().userCounter(userId);
|
var userCounterList = await SCAccountRepository().userCounter(userId);
|
||||||
@ -317,6 +366,8 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
routeObserver.unsubscribe(this);
|
||||||
|
_routeObserverRoute = null;
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_permission_utils.dart';
|
import 'package:yumi/shared/tools/sc_permission_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_cp_relation_level_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/models/message/room_rocket_launch_broadcast_message.dart';
|
import 'package:yumi/shared/data_sources/models/message/room_rocket_launch_broadcast_message.dart';
|
||||||
import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
|
import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
|
||||||
@ -1710,6 +1711,44 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void refreshRoomMicRelationProfilesForUsers(Set<String> userIds) {
|
||||||
|
final roomId = (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (roomId.isEmpty || roomWheatMap.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final normalizedUserIds =
|
||||||
|
userIds.map((userId) => userId.trim()).where((userId) {
|
||||||
|
return userId.isNotEmpty;
|
||||||
|
}).toSet();
|
||||||
|
final refreshAll = normalizedUserIds.isEmpty;
|
||||||
|
if (refreshAll) {
|
||||||
|
_roomMicRelationCache.clear();
|
||||||
|
_roomMicRelationLoadingUserIds.clear();
|
||||||
|
} else {
|
||||||
|
_roomMicRelationCache.removeWhere((userId, _) {
|
||||||
|
return normalizedUserIds.contains(userId);
|
||||||
|
});
|
||||||
|
_roomMicRelationLoadingUserIds.removeWhere(normalizedUserIds.contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
final queuedUserIds = <String>{};
|
||||||
|
for (final mic in roomWheatMap.values) {
|
||||||
|
final user = mic.user;
|
||||||
|
final userId = _roomMicRelationUserId(user);
|
||||||
|
if (user == null || userId.isEmpty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!refreshAll && !normalizedUserIds.contains(userId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!queuedUserIds.add(userId) ||
|
||||||
|
!_roomMicRelationLoadingUserIds.add(userId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unawaited(_hydrateRoomMicRelationProfile(roomId, userId, user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool _mergeCachedRoomMicRelationProfiles() {
|
bool _mergeCachedRoomMicRelationProfiles() {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
_roomMicRelationCache.removeWhere(
|
_roomMicRelationCache.removeWhere(
|
||||||
@ -1806,7 +1845,11 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
relationType: normalizedType,
|
relationType: normalizedType,
|
||||||
cpValue: pair.cpValue ?? cabin?.cpVal?.toDouble(),
|
cpValue: pair.cpValue ?? cabin?.cpVal?.toDouble(),
|
||||||
levelText:
|
levelText:
|
||||||
pair.levelText ?? _roomMicRelationLevelTextFromCabin(cabin),
|
scCpRelationLevelTextFromCabin(
|
||||||
|
cabin,
|
||||||
|
fallbackText: pair.levelText,
|
||||||
|
) ??
|
||||||
|
_roomMicRelationLevelTextFromCabin(cabin),
|
||||||
);
|
);
|
||||||
if (_isRoomMicRelationLocallyDismissed(enrichedPair)) {
|
if (_isRoomMicRelationLocallyDismissed(enrichedPair)) {
|
||||||
return;
|
return;
|
||||||
@ -1954,6 +1997,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
pair.cpAccount?.trim() ?? "",
|
pair.cpAccount?.trim() ?? "",
|
||||||
pair.relationType?.trim() ?? "",
|
pair.relationType?.trim() ?? "",
|
||||||
pair.status?.trim() ?? "",
|
pair.status?.trim() ?? "",
|
||||||
|
pair.days?.trim() ?? "",
|
||||||
|
pair.levelText?.trim() ?? "",
|
||||||
|
pair.cpValue?.toString() ?? "",
|
||||||
|
pair.dismissCost?.toString() ?? "",
|
||||||
].join("|");
|
].join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4101,6 +4148,67 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomRocketPostLaunchRefreshTimers.clear();
|
_roomRocketPostLaunchRefreshTimers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void advanceRoomRocketStatusAfterLaunch(
|
||||||
|
RoomRocketLaunchBroadcastMessage launch,
|
||||||
|
) {
|
||||||
|
final currentRoomId =
|
||||||
|
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (!launch.isValid || launch.roomId != currentRoomId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final currentStatus = roomRocketStatus;
|
||||||
|
if (currentStatus == null || !currentStatus.isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final nextLevel = _nextRoomRocketLevelAfterLaunch(launch.safeLevel);
|
||||||
|
final nextLevelInfo = _roomRocketLevelFor(nextLevel);
|
||||||
|
final update = SCRoomRocketStatusRes(
|
||||||
|
roomId: launch.roomId,
|
||||||
|
level: nextLevel,
|
||||||
|
currentLevel: nextLevel,
|
||||||
|
currentEnergy: 0,
|
||||||
|
maxEnergy: nextLevelInfo?.needEnergy,
|
||||||
|
needEnergy: nextLevelInfo?.needEnergy,
|
||||||
|
energyPercent: 0,
|
||||||
|
displayPercent: 0,
|
||||||
|
configured: currentStatus.configured,
|
||||||
|
enabled: currentStatus.enabled,
|
||||||
|
levels: currentStatus.levels,
|
||||||
|
);
|
||||||
|
roomRocketStatus = currentStatus.mergeRealtimeUpdate(
|
||||||
|
update,
|
||||||
|
roomId: launch.roomId,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _nextRoomRocketLevelAfterLaunch(int launchedLevel) {
|
||||||
|
final normalizedLevel = launchedLevel.clamp(1, 99).toInt();
|
||||||
|
final levels = [
|
||||||
|
...(roomRocketStatus?.levels ?? const <SCRoomRocketLevelRes>[]),
|
||||||
|
]..sort((a, b) {
|
||||||
|
final sortCompare = a.sort.compareTo(b.sort);
|
||||||
|
return sortCompare != 0 ? sortCompare : a.level.compareTo(b.level);
|
||||||
|
});
|
||||||
|
for (final level in levels) {
|
||||||
|
if (level.enabled && level.level > normalizedLevel) {
|
||||||
|
return level.level.clamp(1, 99).toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return normalizedLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRoomRocketLevelRes? _roomRocketLevelFor(int level) {
|
||||||
|
final normalizedLevel = level.clamp(1, 99).toInt();
|
||||||
|
for (final item
|
||||||
|
in roomRocketStatus?.levels ?? const <SCRoomRocketLevelRes>[]) {
|
||||||
|
if (item.level == normalizedLevel) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleRoomRocketLaunchBroadcast(
|
void handleRoomRocketLaunchBroadcast(
|
||||||
RoomRocketLaunchBroadcastMessage launch,
|
RoomRocketLaunchBroadcastMessage launch,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import 'package:yumi/shared/tools/sc_message_utils.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_broadcast_assets.dart';
|
import 'package:yumi/shared/tools/sc_cp_relation_broadcast_assets.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_rocket_reward_popup_ack.dart';
|
import 'package:yumi/shared/tools/sc_room_rocket_reward_popup_ack.dart';
|
||||||
@ -134,6 +135,74 @@ class _PendingRoomRocketRewardPopup {
|
|||||||
final List<SCRoomRocketRewardRecordRes> initialRoomRecords;
|
final List<SCRoomRocketRewardRecordRes> initialRoomRecords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool roomRocketRewardPopupTargetsCurrentUser({
|
||||||
|
required Map<String, dynamic> payload,
|
||||||
|
required Set<String> currentUserKeys,
|
||||||
|
}) {
|
||||||
|
final audienceUserIds = _roomRocketRewardPopupAudienceUserIds(payload);
|
||||||
|
if (audienceUserIds == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (audienceUserIds.isEmpty || currentUserKeys.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return currentUserKeys.any(audienceUserIds.contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String>? _roomRocketRewardPopupAudienceUserIds(
|
||||||
|
Map<String, dynamic> payload,
|
||||||
|
) {
|
||||||
|
for (final key in const [
|
||||||
|
'popupUserIds',
|
||||||
|
'popup_user_ids',
|
||||||
|
'notifyUserIds',
|
||||||
|
'notify_user_ids',
|
||||||
|
'audienceUserIds',
|
||||||
|
'audience_user_ids',
|
||||||
|
'visibleUserIds',
|
||||||
|
'visible_user_ids',
|
||||||
|
]) {
|
||||||
|
if (payload.containsKey(key)) {
|
||||||
|
return _roomRocketPayloadStringSet(payload[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _roomRocketPayloadStringSet(dynamic value) {
|
||||||
|
if (value == null) {
|
||||||
|
return <String>{};
|
||||||
|
}
|
||||||
|
if (value is Iterable && value is! String) {
|
||||||
|
return value
|
||||||
|
.map((item) => item?.toString().trim() ?? '')
|
||||||
|
.where((item) => item.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
}
|
||||||
|
if (value is Map) {
|
||||||
|
return value.values
|
||||||
|
.map((item) => item?.toString().trim() ?? '')
|
||||||
|
.where((item) => item.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
}
|
||||||
|
final raw = value.toString().trim();
|
||||||
|
if (raw.isEmpty) {
|
||||||
|
return <String>{};
|
||||||
|
}
|
||||||
|
if (raw.startsWith('[') || raw.startsWith('{')) {
|
||||||
|
try {
|
||||||
|
return _roomRocketPayloadStringSet(jsonDecode(raw));
|
||||||
|
} catch (_) {
|
||||||
|
return <String>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.where((item) => item.isNotEmpty)
|
||||||
|
.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
class RealTimeMessagingManager extends ChangeNotifier {
|
class RealTimeMessagingManager extends ChangeNotifier {
|
||||||
static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog';
|
static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog';
|
||||||
|
|
||||||
@ -146,11 +215,18 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
static const int _luckyGiftBurstMergeWindowMs = 1800;
|
static const int _luckyGiftBurstMergeWindowMs = 1800;
|
||||||
static const int _luckyGiftBurstRecentEventTtlMs = 5000;
|
static const int _luckyGiftBurstRecentEventTtlMs = 5000;
|
||||||
static const int _luckyGiftBurstPlaybackWatchdogMs = 8000;
|
static const int _luckyGiftBurstPlaybackWatchdogMs = 8000;
|
||||||
|
static const int _cpRelationGiftRefreshMinIntervalMs = 800;
|
||||||
|
static const List<Duration> _cpRelationGiftRefreshDelays = [
|
||||||
|
Duration(milliseconds: 300),
|
||||||
|
Duration(milliseconds: 1500),
|
||||||
|
Duration(milliseconds: 3500),
|
||||||
|
];
|
||||||
static const int _roomRocketLaunchRecentTtlMs = 90000;
|
static const int _roomRocketLaunchRecentTtlMs = 90000;
|
||||||
static const int _roomRocketLaunchLooseRecentTtlMs = 12000;
|
static const int _roomRocketLaunchLooseRecentTtlMs = 12000;
|
||||||
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
|
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
|
||||||
static const int _roomRocketRewardPendingTtlMs = 3600000;
|
static const int _roomRocketRewardPendingTtlMs = 3600000;
|
||||||
static const int _roomRocketRewardProbeTtlMs = 3600000;
|
static const int _roomRocketRewardProbeTtlMs = 3600000;
|
||||||
|
static const int _roomRocketEmptyRewardPopupRecentTtlMs = 120000;
|
||||||
static const int _roomRocketLaunchCountdownRetainMs = 10000;
|
static const int _roomRocketLaunchCountdownRetainMs = 10000;
|
||||||
static const int _roomRocketLaunchEffectConsumedTtlMs = 120000;
|
static const int _roomRocketLaunchEffectConsumedTtlMs = 120000;
|
||||||
static const Duration _createRoomGroupTimeout = Duration(seconds: 12);
|
static const Duration _createRoomGroupTimeout = Duration(seconds: 12);
|
||||||
@ -296,12 +372,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
final Map<String, int> _roomRocketRewardHoldUntilTimes = <String, int>{};
|
final Map<String, int> _roomRocketRewardHoldUntilTimes = <String, int>{};
|
||||||
final Map<String, Timer> _roomRocketRewardHoldTimers = <String, Timer>{};
|
final Map<String, Timer> _roomRocketRewardHoldTimers = <String, Timer>{};
|
||||||
final Set<String> _roomRocketRewardPopupLoadingRoomIds = <String>{};
|
final Set<String> _roomRocketRewardPopupLoadingRoomIds = <String>{};
|
||||||
|
final Map<String, int> _shownRoomRocketEmptyRewardPopupTimes =
|
||||||
|
<String, int>{};
|
||||||
Timer? _roomRocketRewardPopupRetryTimer;
|
Timer? _roomRocketRewardPopupRetryTimer;
|
||||||
Timer? _roomRocketRoomReadyRewardRetryTimer;
|
Timer? _roomRocketRoomReadyRewardRetryTimer;
|
||||||
final Set<String> _shownCpInviteMessageKeys = <String>{};
|
final Set<String> _shownCpInviteMessageKeys = <String>{};
|
||||||
final Set<String> _shownCpInviteResultMessageKeys = <String>{};
|
final Set<String> _shownCpInviteResultMessageKeys = <String>{};
|
||||||
final Set<String> _shownCpInviteRejectedDialogKeys = <String>{};
|
final Set<String> _shownCpInviteRejectedDialogKeys = <String>{};
|
||||||
final Map<String, int> _recentCpRelationNoticeTimes = <String, int>{};
|
final Map<String, int> _recentCpRelationNoticeTimes = <String, int>{};
|
||||||
|
int _lastCpRelationGiftRefreshAt = 0;
|
||||||
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
|
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
|
||||||
String? roomRedPacketBroadcastGroupId;
|
String? roomRedPacketBroadcastGroupId;
|
||||||
String? roomRedPacketBroadcastRegionCode;
|
String? roomRedPacketBroadcastRegionCode;
|
||||||
@ -2693,6 +2772,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
|
|
||||||
addMsg(Msg msg) {
|
addMsg(Msg msg) {
|
||||||
_scheduleRoomRocketStatusRefreshForGiftMessage(msg);
|
_scheduleRoomRocketStatusRefreshForGiftMessage(msg);
|
||||||
|
_scheduleCpRelationRefreshForGiftMessage(msg);
|
||||||
final mergedGiftMsg = _mergeGiftMessageIfNeeded(msg);
|
final mergedGiftMsg = _mergeGiftMessageIfNeeded(msg);
|
||||||
if (mergedGiftMsg != null) {
|
if (mergedGiftMsg != null) {
|
||||||
msgAllListener?.call(mergedGiftMsg);
|
msgAllListener?.call(mergedGiftMsg);
|
||||||
@ -3083,6 +3163,64 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
rtcProvider.scheduleRoomRocketStatusRefreshForGift(roomId: currentRoomId);
|
rtcProvider.scheduleRoomRocketStatusRefreshForGift(roomId: currentRoomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _scheduleCpRelationRefreshForGiftMessage(Msg msg) {
|
||||||
|
if (!_isCpRelationGiftMessage(msg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
if (now - _lastCpRelationGiftRefreshAt <
|
||||||
|
_cpRelationGiftRefreshMinIntervalMs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_lastCpRelationGiftRefreshAt = now;
|
||||||
|
final userIds = Set<String>.unmodifiable(_cpRelationGiftUserIds(msg));
|
||||||
|
for (final delay in _cpRelationGiftRefreshDelays) {
|
||||||
|
unawaited(
|
||||||
|
Future<void>.delayed(delay, () {
|
||||||
|
_refreshCpRelationDataAfterGift(userIds);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isCpRelationGiftMessage(Msg msg) {
|
||||||
|
if (msg.type != SCRoomMsgType.gift &&
|
||||||
|
msg.type != SCRoomMsgType.luckGiftAnimOther) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return scIsCpGift(msg.gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> _cpRelationGiftUserIds(Msg msg) {
|
||||||
|
return <String>{
|
||||||
|
msg.user?.id?.trim() ?? '',
|
||||||
|
msg.toUser?.id?.trim() ?? '',
|
||||||
|
...?msg.targetUserIds?.map((userId) => userId.trim()),
|
||||||
|
}..removeWhere((userId) => userId.isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshCpRelationDataAfterGift(Set<String> userIds) {
|
||||||
|
final currentContext =
|
||||||
|
context?.mounted == true ? context : navigatorKey.currentState?.context;
|
||||||
|
if (currentContext == null || !currentContext.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
unawaited(
|
||||||
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
|
currentContext,
|
||||||
|
listen: false,
|
||||||
|
).refreshLoadedCpProfiles(),
|
||||||
|
);
|
||||||
|
} catch (_) {}
|
||||||
|
try {
|
||||||
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
currentContext,
|
||||||
|
listen: false,
|
||||||
|
).refreshRoomMicRelationProfilesForUsers(userIds);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
void _cacheRoomRedPacketMessage(Msg msg) {
|
void _cacheRoomRedPacketMessage(Msg msg) {
|
||||||
try {
|
try {
|
||||||
final decoded = jsonDecode(msg.msg ?? '');
|
final decoded = jsonDecode(msg.msg ?? '');
|
||||||
@ -3865,6 +4003,13 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isRoomRocketStatusUpdateType(dynamic type) {
|
||||||
|
final value = type?.toString().trim();
|
||||||
|
return value == SCRoomMsgType.voiceRoomRocketStatusUpdate ||
|
||||||
|
value == SCRoomMsgType.rocketEnergyUpdate ||
|
||||||
|
value == 'ROCKET_ENERGY_UPDATE';
|
||||||
|
}
|
||||||
|
|
||||||
void _handleVoiceRoomRocketStatusUpdate(dynamic payload) {
|
void _handleVoiceRoomRocketStatusUpdate(dynamic payload) {
|
||||||
final data = _broadcastPayloadMap(payload);
|
final data = _broadcastPayloadMap(payload);
|
||||||
final roomId = _payloadRoomId(data);
|
final roomId = _payloadRoomId(data);
|
||||||
@ -4006,6 +4151,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_scheduleRoomRocketRewardPopupAfterHold(
|
_scheduleRoomRocketRewardPopupAfterHold(
|
||||||
resolvedRoomId,
|
resolvedRoomId,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: true,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4016,89 +4162,21 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_showRoomRocketRewardPopupDialog(
|
_showRoomRocketRewardPopupDialog(
|
||||||
resolvedRoomId,
|
resolvedRoomId,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isCurrentUserInRoomRocketRewardPopup(Map<String, dynamic> payload) {
|
bool _isCurrentUserInRoomRocketRewardPopup(Map<String, dynamic> payload) {
|
||||||
final userIds = _roomRocketRewardPopupUserIds(payload);
|
|
||||||
if (userIds == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (userIds.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final profile = AccountStorage().getCurrentUser()?.userProfile;
|
final profile = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
final currentKeys = <String>{
|
final currentKeys = <String>{
|
||||||
profile?.id?.trim() ?? '',
|
profile?.id?.trim() ?? '',
|
||||||
profile?.account?.trim() ?? '',
|
profile?.account?.trim() ?? '',
|
||||||
}..removeWhere((item) => item.isEmpty);
|
}..removeWhere((item) => item.isEmpty);
|
||||||
if (currentKeys.isEmpty) {
|
return roomRocketRewardPopupTargetsCurrentUser(
|
||||||
return false;
|
payload: payload,
|
||||||
}
|
currentUserKeys: currentKeys,
|
||||||
return currentKeys.any(userIds.contains);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
Set<String>? _roomRocketRewardPopupUserIds(Map<String, dynamic> payload) {
|
|
||||||
for (final key in const [
|
|
||||||
'userIds',
|
|
||||||
'user_ids',
|
|
||||||
'targetUserIds',
|
|
||||||
'target_user_ids',
|
|
||||||
'winnerUserIds',
|
|
||||||
'winner_user_ids',
|
|
||||||
'userId',
|
|
||||||
'user_id',
|
|
||||||
'winnerUserId',
|
|
||||||
'winner_user_id',
|
|
||||||
'rewardUserId',
|
|
||||||
'reward_user_id',
|
|
||||||
'receiverUserId',
|
|
||||||
'receiver_user_id',
|
|
||||||
'targetUserId',
|
|
||||||
'target_user_id',
|
|
||||||
'toUserId',
|
|
||||||
'to_user_id',
|
|
||||||
]) {
|
|
||||||
if (payload.containsKey(key)) {
|
|
||||||
return _payloadStringSet(payload[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> _payloadStringSet(dynamic value) {
|
|
||||||
if (value == null) {
|
|
||||||
return <String>{};
|
|
||||||
}
|
|
||||||
if (value is Iterable && value is! String) {
|
|
||||||
return value
|
|
||||||
.map((item) => item?.toString().trim() ?? '')
|
|
||||||
.where((item) => item.isNotEmpty)
|
|
||||||
.toSet();
|
|
||||||
}
|
|
||||||
if (value is Map) {
|
|
||||||
return value.values
|
|
||||||
.map((item) => item?.toString().trim() ?? '')
|
|
||||||
.where((item) => item.isNotEmpty)
|
|
||||||
.toSet();
|
|
||||||
}
|
|
||||||
final raw = value.toString().trim();
|
|
||||||
if (raw.isEmpty) {
|
|
||||||
return <String>{};
|
|
||||||
}
|
|
||||||
if (raw.startsWith('[') || raw.startsWith('{')) {
|
|
||||||
try {
|
|
||||||
return _payloadStringSet(jsonDecode(raw));
|
|
||||||
} catch (_) {
|
|
||||||
return <String>{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return raw
|
|
||||||
.split(',')
|
|
||||||
.map((item) => item.trim())
|
|
||||||
.where((item) => item.isNotEmpty)
|
|
||||||
.toSet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryVoiceRoomRocketRewardPopups(String roomId) {
|
void queryVoiceRoomRocketRewardPopups(String roomId) {
|
||||||
@ -4138,6 +4216,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
final pendingAfterResume = _pendingRoomRocketRewardPopups[normalizedRoomId];
|
final pendingAfterResume = _pendingRoomRocketRewardPopups[normalizedRoomId];
|
||||||
final hasProbeAfterResume =
|
final hasProbeAfterResume =
|
||||||
_recentRoomRocketRewardProbeTimes[normalizedRoomId] != null;
|
_recentRoomRocketRewardProbeTimes[normalizedRoomId] != null;
|
||||||
|
final allowEmptyPopup = pendingAfterResume != null || hasProbeAfterResume;
|
||||||
if (pendingAfterResume == null && !hasProbeAfterResume && !forceQuery) {
|
if (pendingAfterResume == null && !hasProbeAfterResume && !forceQuery) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4162,6 +4241,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
initialRoomRecords:
|
initialRoomRecords:
|
||||||
pending?.initialRoomRecords ??
|
pending?.initialRoomRecords ??
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4171,6 +4251,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
initialRoomRecords:
|
initialRoomRecords:
|
||||||
pendingAfterResume?.initialRoomRecords ??
|
pendingAfterResume?.initialRoomRecords ??
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -4338,6 +4419,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
String roomId, {
|
String roomId, {
|
||||||
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
bool allowEmptyPopup = false,
|
||||||
}) {
|
}) {
|
||||||
final normalizedRoomId = roomId.trim();
|
final normalizedRoomId = roomId.trim();
|
||||||
if (normalizedRoomId.isEmpty) {
|
if (normalizedRoomId.isEmpty) {
|
||||||
@ -4356,7 +4438,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
() {
|
() {
|
||||||
_roomRocketRewardHoldTimers.remove(normalizedRoomId);
|
_roomRocketRewardHoldTimers.remove(normalizedRoomId);
|
||||||
if (_isRoomRocketRewardPopupHeld(normalizedRoomId)) {
|
if (_isRoomRocketRewardPopupHeld(normalizedRoomId)) {
|
||||||
_scheduleRoomRocketRewardPopupAfterHold(normalizedRoomId);
|
_scheduleRoomRocketRewardPopupAfterHold(
|
||||||
|
normalizedRoomId,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
|
if (!_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
|
||||||
@ -4371,6 +4456,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
initialRoomRecords:
|
initialRoomRecords:
|
||||||
pending?.initialRoomRecords ??
|
pending?.initialRoomRecords ??
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -4381,6 +4467,55 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _markRoomRocketEmptyRewardPopupForShow(
|
||||||
|
String roomId,
|
||||||
|
List<SCRoomRocketRewardRecordRes> roomRecords,
|
||||||
|
) {
|
||||||
|
final normalizedRoomId = roomId.trim();
|
||||||
|
if (normalizedRoomId.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
_pruneRoomRocketRewardFallbacks(now);
|
||||||
|
final key = _roomRocketEmptyRewardPopupKey(normalizedRoomId, roomRecords);
|
||||||
|
if (_shownRoomRocketEmptyRewardPopupTimes.containsKey(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_shownRoomRocketEmptyRewardPopupTimes[key] = now;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _roomRocketEmptyRewardPopupKey(
|
||||||
|
String roomId,
|
||||||
|
List<SCRoomRocketRewardRecordRes> roomRecords,
|
||||||
|
) {
|
||||||
|
final launchNos =
|
||||||
|
roomRecords
|
||||||
|
.map((record) => record.launchNo.trim())
|
||||||
|
.where((launchNo) => launchNo.isNotEmpty)
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort();
|
||||||
|
if (launchNos.isNotEmpty) {
|
||||||
|
return 'room_rocket_empty_reward|$roomId|launch:${launchNos.join(",")}';
|
||||||
|
}
|
||||||
|
final roundKeys =
|
||||||
|
roomRecords
|
||||||
|
.map((record) {
|
||||||
|
final level = record.level > 0 ? record.level.toString() : '';
|
||||||
|
final round = record.roundNo > 0 ? record.roundNo.toString() : '';
|
||||||
|
return [level, round].where((item) => item.isNotEmpty).join(':');
|
||||||
|
})
|
||||||
|
.where((key) => key.isNotEmpty)
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort();
|
||||||
|
if (roundKeys.isNotEmpty) {
|
||||||
|
return 'room_rocket_empty_reward|$roomId|round:${roundKeys.join(",")}';
|
||||||
|
}
|
||||||
|
return 'room_rocket_empty_reward|$roomId|recent';
|
||||||
|
}
|
||||||
|
|
||||||
void _pruneRoomRocketRewardFallbacks(int now) {
|
void _pruneRoomRocketRewardFallbacks(int now) {
|
||||||
_pendingRoomRocketRewardPopups.removeWhere(
|
_pendingRoomRocketRewardPopups.removeWhere(
|
||||||
(_, pending) => now - pending.receivedAt > _roomRocketRewardPendingTtlMs,
|
(_, pending) => now - pending.receivedAt > _roomRocketRewardPendingTtlMs,
|
||||||
@ -4388,11 +4523,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_recentRoomRocketRewardProbeTimes.removeWhere(
|
_recentRoomRocketRewardProbeTimes.removeWhere(
|
||||||
(_, timestamp) => now - timestamp > _roomRocketRewardProbeTtlMs,
|
(_, timestamp) => now - timestamp > _roomRocketRewardProbeTtlMs,
|
||||||
);
|
);
|
||||||
|
_shownRoomRocketEmptyRewardPopupTimes.removeWhere(
|
||||||
|
(_, timestamp) =>
|
||||||
|
now - timestamp > _roomRocketEmptyRewardPopupRecentTtlMs,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearRoomRocketRewardFallbackState() {
|
void _clearRoomRocketRewardFallbackState() {
|
||||||
_pendingRoomRocketRewardPopups.clear();
|
_pendingRoomRocketRewardPopups.clear();
|
||||||
_recentRoomRocketRewardProbeTimes.clear();
|
_recentRoomRocketRewardProbeTimes.clear();
|
||||||
|
_shownRoomRocketEmptyRewardPopupTimes.clear();
|
||||||
_consumedRoomRocketLaunchEffectTimes.clear();
|
_consumedRoomRocketLaunchEffectTimes.clear();
|
||||||
_hiddenCurrentRoomRocketLaunchKeys.clear();
|
_hiddenCurrentRoomRocketLaunchKeys.clear();
|
||||||
_roomRocketRoomReadyRewardRetryTimer?.cancel();
|
_roomRocketRoomReadyRewardRetryTimer?.cancel();
|
||||||
@ -4484,6 +4624,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_recentRoomRocketRewardProbeTimes.remove(launch.roomId);
|
_recentRoomRocketRewardProbeTimes.remove(launch.roomId);
|
||||||
_markRoomRocketLaunchEffectConsumed(launch);
|
_markRoomRocketLaunchEffectConsumed(launch);
|
||||||
_holdRoomRocketRewardPopupUntilLaunchEnds(launch);
|
_holdRoomRocketRewardPopupUntilLaunchEnds(launch);
|
||||||
|
rtcProvider.advanceRoomRocketStatusAfterLaunch(launch);
|
||||||
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
||||||
if (shouldFetchReward) {
|
if (shouldFetchReward) {
|
||||||
_scheduleRoomRocketRewardPopupAfterHold(
|
_scheduleRoomRocketRewardPopupAfterHold(
|
||||||
@ -4491,6 +4632,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
initialRoomRecords:
|
initialRoomRecords:
|
||||||
pending?.initialRoomRecords ??
|
pending?.initialRoomRecords ??
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
allowEmptyPopup: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4772,6 +4914,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
int attempt = 0,
|
int attempt = 0,
|
||||||
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
bool allowEmptyPopup = false,
|
||||||
}) async {
|
}) async {
|
||||||
final normalizedRoomId = roomId.trim();
|
final normalizedRoomId = roomId.trim();
|
||||||
if (normalizedRoomId.isEmpty ||
|
if (normalizedRoomId.isEmpty ||
|
||||||
@ -4782,6 +4925,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_scheduleRoomRocketRewardPopupAfterHold(
|
_scheduleRoomRocketRewardPopupAfterHold(
|
||||||
normalizedRoomId,
|
normalizedRoomId,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4792,6 +4936,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
normalizedRoomId,
|
normalizedRoomId,
|
||||||
attempt: attempt,
|
attempt: attempt,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -4814,6 +4959,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
normalizedRoomId,
|
normalizedRoomId,
|
||||||
attempt,
|
attempt,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4823,7 +4969,17 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
if (res.records.isNotEmpty && popupRecords.isEmpty) {
|
if (res.records.isNotEmpty && popupRecords.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (popupRecords.isEmpty && initialRoomRecords.isEmpty) {
|
if (popupRecords.isEmpty &&
|
||||||
|
initialRoomRecords.isEmpty &&
|
||||||
|
!allowEmptyPopup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (popupRecords.isEmpty &&
|
||||||
|
allowEmptyPopup &&
|
||||||
|
!_markRoomRocketEmptyRewardPopupForShow(
|
||||||
|
normalizedRoomId,
|
||||||
|
initialRoomRecords,
|
||||||
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_roomRocketRewardPopupRetryTimer?.cancel();
|
_roomRocketRewardPopupRetryTimer?.cancel();
|
||||||
@ -4851,6 +5007,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
normalizedRoomId,
|
normalizedRoomId,
|
||||||
attempt,
|
attempt,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
_roomRocketRewardPopupLoadingRoomIds.remove(normalizedRoomId);
|
_roomRocketRewardPopupLoadingRoomIds.remove(normalizedRoomId);
|
||||||
@ -4931,6 +5088,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
int attempt, {
|
int attempt, {
|
||||||
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
List<SCRoomRocketRewardRecordRes> initialRoomRecords =
|
||||||
const <SCRoomRocketRewardRecordRes>[],
|
const <SCRoomRocketRewardRecordRes>[],
|
||||||
|
bool allowEmptyPopup = false,
|
||||||
}) {
|
}) {
|
||||||
if (attempt >= _roomRocketRewardPopupRetryDelays.length ||
|
if (attempt >= _roomRocketRewardPopupRetryDelays.length ||
|
||||||
!_isCurrentVisibleVoiceRoom(roomId)) {
|
!_isCurrentVisibleVoiceRoom(roomId)) {
|
||||||
@ -4946,6 +5104,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
roomId,
|
roomId,
|
||||||
attempt: attempt + 1,
|
attempt: attempt + 1,
|
||||||
initialRoomRecords: initialRoomRecords,
|
initialRoomRecords: initialRoomRecords,
|
||||||
|
allowEmptyPopup: allowEmptyPopup,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -4988,6 +5147,23 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _scheduleRoomRocketStatusRefreshForCurrentRoom(String roomId) {
|
||||||
|
final normalizedRoomId = roomId.trim();
|
||||||
|
if (normalizedRoomId.isEmpty || !_isCurrentVoiceRoom(normalizedRoomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final currentContext = context;
|
||||||
|
if (currentContext == null || !currentContext.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
currentContext,
|
||||||
|
listen: false,
|
||||||
|
).scheduleRoomRocketStatusRefreshForGift(roomId: normalizedRoomId);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
List<SCRoomRocketRewardRecordRes> _roomRocketRewardRecordsFromPayload(
|
List<SCRoomRocketRewardRecordRes> _roomRocketRewardRecordsFromPayload(
|
||||||
Map<String, dynamic> payload,
|
Map<String, dynamic> payload,
|
||||||
) {
|
) {
|
||||||
@ -5248,6 +5424,12 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
final value = _payloadNum(
|
final value = _payloadNum(
|
||||||
payload['currentLevel'] ??
|
payload['currentLevel'] ??
|
||||||
payload['current_level'] ??
|
payload['current_level'] ??
|
||||||
|
payload['currentRocketLevel'] ??
|
||||||
|
payload['current_rocket_level'] ??
|
||||||
|
payload['rocketCurrentLevel'] ??
|
||||||
|
payload['rocket_current_level'] ??
|
||||||
|
payload['levelNo'] ??
|
||||||
|
payload['level_no'] ??
|
||||||
payload['level'] ??
|
payload['level'] ??
|
||||||
payload['rocketLevel'] ??
|
payload['rocketLevel'] ??
|
||||||
payload['rocket_level'] ??
|
payload['rocket_level'] ??
|
||||||
@ -5548,12 +5730,13 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
priority: 1000,
|
priority: 1000,
|
||||||
);
|
);
|
||||||
OverlayManager().addMessage(msg);
|
OverlayManager().addMessage(msg);
|
||||||
|
_scheduleRoomRocketStatusRefreshForCurrentRoom(launchRoomId);
|
||||||
} else if (type == SCRoomMsgType.voiceRoomRocketLaunchBroadcast) {
|
} else if (type == SCRoomMsgType.voiceRoomRocketLaunchBroadcast) {
|
||||||
_handleVoiceRoomRocketLaunchBroadcast(
|
_handleVoiceRoomRocketLaunchBroadcast(
|
||||||
data["data"],
|
data["data"],
|
||||||
isRegionBroadcast: isRegionBroadcastGroup,
|
isRegionBroadcast: isRegionBroadcastGroup,
|
||||||
);
|
);
|
||||||
} else if (type == SCRoomMsgType.voiceRoomRocketStatusUpdate) {
|
} else if (_isRoomRocketStatusUpdateType(type)) {
|
||||||
_handleVoiceRoomRocketStatusUpdate(data["data"]);
|
_handleVoiceRoomRocketStatusUpdate(data["data"]);
|
||||||
} else if (type == SCRoomMsgType.voiceRoomRocketRewardPopup) {
|
} else if (type == SCRoomMsgType.voiceRoomRocketRewardPopup) {
|
||||||
_handleVoiceRoomRocketRewardPopup(data["data"] ?? data);
|
_handleVoiceRoomRocketRewardPopup(data["data"] ?? data);
|
||||||
@ -5651,7 +5834,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
_handleVoiceRoomRocketLaunchBroadcast(data["data"]);
|
_handleVoiceRoomRocketLaunchBroadcast(data["data"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data["type"] == SCRoomMsgType.voiceRoomRocketStatusUpdate) {
|
if (_isRoomRocketStatusUpdateType(data["type"])) {
|
||||||
_handleVoiceRoomRocketStatusUpdate(data["data"]);
|
_handleVoiceRoomRocketStatusUpdate(data["data"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,14 +132,32 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
|
|
||||||
/// 当前正在查看的个人详情资料,不用于承载登录用户自己的 Me 页数据。
|
/// 当前正在查看的个人详情资料,不用于承载登录用户自己的 Me 页数据。
|
||||||
SocialChatUserProfile? userProfile;
|
SocialChatUserProfile? userProfile;
|
||||||
|
int _userInfoRequestSerial = 0;
|
||||||
|
|
||||||
void getUserInfoById(String userId) async {
|
Future<void> getUserInfoById(String userId) {
|
||||||
userProfile = null;
|
return _loadUserInfoById(userId, clearBeforeLoad: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reloadUserInfoById(String userId) {
|
||||||
|
return _loadUserInfoById(userId, clearBeforeLoad: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadUserInfoById(
|
||||||
|
String userId, {
|
||||||
|
required bool clearBeforeLoad,
|
||||||
|
}) async {
|
||||||
|
final requestSerial = ++_userInfoRequestSerial;
|
||||||
|
if (clearBeforeLoad) {
|
||||||
|
userProfile = null;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
final loadedProfile = await SCAccountRepository().loadUserInfo(userId);
|
final loadedProfile = await SCAccountRepository().loadUserInfo(userId);
|
||||||
var mergedProfile = await _withCurrentVipLevel(loadedProfile, userId);
|
var mergedProfile = await _withCurrentVipLevel(loadedProfile, userId);
|
||||||
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
|
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
|
||||||
|
if (requestSerial != _userInfoRequestSerial) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
userProfile = mergedProfile;
|
userProfile = mergedProfile;
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +211,41 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
final cpPairs = <CPRes>[];
|
final cpPairs = <CPRes>[];
|
||||||
final closeFriendPairs = <CPRes>[];
|
final closeFriendPairs = <CPRes>[];
|
||||||
|
_addCpRelationPairs(
|
||||||
|
cpPairs: cpPairs,
|
||||||
|
closeFriendPairs: closeFriendPairs,
|
||||||
|
relations: fallbackCpPairs,
|
||||||
|
);
|
||||||
|
_addCpRelationPairs(
|
||||||
|
cpPairs: cpPairs,
|
||||||
|
closeFriendPairs: closeFriendPairs,
|
||||||
|
relations: fallbackCloseFriendPairs,
|
||||||
|
);
|
||||||
var loadedAnyCabin = false;
|
var loadedAnyCabin = false;
|
||||||
|
final shouldLoadCurrentUserPairs = _isCurrentUserId(userId);
|
||||||
|
if (kDebugMode) {
|
||||||
|
final currentUserId =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
||||||
|
debugPrint(
|
||||||
|
'[CP][Profile] source userId=$userId currentUserId=$currentUserId '
|
||||||
|
'profileCp=${profile.cpList?.length ?? 0} '
|
||||||
|
'profileCloseFriends=${profile.closeFriendList?.length ?? 0} '
|
||||||
|
'loadCurrentPairs=$shouldLoadCurrentUserPairs',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (shouldLoadCurrentUserPairs) {
|
||||||
|
await Future.wait(
|
||||||
|
_cpProfileRelationTypes.map((relationType) async {
|
||||||
|
final pairs = await _safeLoadCpRelationshipPairs(relationType);
|
||||||
|
_addCpRelationPairs(
|
||||||
|
cpPairs: cpPairs,
|
||||||
|
closeFriendPairs: closeFriendPairs,
|
||||||
|
relations: pairs,
|
||||||
|
fallbackRelationType: relationType,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
await Future.wait(
|
await Future.wait(
|
||||||
_cpProfileRelationTypes.map((relationType) async {
|
_cpProfileRelationTypes.map((relationType) async {
|
||||||
final cabin = await _safeLoadCpCabin(userId, relationType);
|
final cabin = await _safeLoadCpCabin(userId, relationType);
|
||||||
@ -239,8 +291,9 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
if (!loadedAnyCabin) {
|
if (!loadedAnyCabin) {
|
||||||
return profile.copyWith(
|
return profile.copyWith(
|
||||||
cpList: fallbackCpPairs,
|
cpList: cpPairs,
|
||||||
closeFriendList: fallbackCloseFriendPairs,
|
closeFriendList: closeFriendPairs,
|
||||||
|
isCpRelation: cpPairs.isNotEmpty,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return profile.copyWith(
|
return profile.copyWith(
|
||||||
@ -325,6 +378,28 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
].join('|');
|
].join('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<CPRes>> _safeLoadCpRelationshipPairs(String relationType) async {
|
||||||
|
try {
|
||||||
|
final pairs = await SCAccountRepository().cpRelationshipPairs(
|
||||||
|
relationType: relationType,
|
||||||
|
);
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(
|
||||||
|
'[CP][Profile] pair loaded relationType=$relationType '
|
||||||
|
'count=${pairs.length}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return pairs;
|
||||||
|
} catch (error) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
debugPrint(
|
||||||
|
'[CP][Profile] pair failed relationType=$relationType error=$error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const <CPRes>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<SCCpCabinRes?> _safeLoadCpCabin(
|
Future<SCCpCabinRes?> _safeLoadCpCabin(
|
||||||
String userId,
|
String userId,
|
||||||
String relationType,
|
String relationType,
|
||||||
@ -493,10 +568,12 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _isCurrentUserId(String userId) {
|
bool _isCurrentUserId(String userId) {
|
||||||
final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
|
final currentUserId =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.id?.trim();
|
||||||
|
final normalizedUserId = userId.trim();
|
||||||
return currentUserId != null &&
|
return currentUserId != null &&
|
||||||
currentUserId.isNotEmpty &&
|
currentUserId.isNotEmpty &&
|
||||||
currentUserId == userId;
|
currentUserId == normalizedUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
||||||
|
|||||||
@ -1187,9 +1187,7 @@ class CPRes {
|
|||||||
source['level'] ??
|
source['level'] ??
|
||||||
source['cpLevel'] ??
|
source['cpLevel'] ??
|
||||||
source['cabinLevel'] ??
|
source['cabinLevel'] ??
|
||||||
source['lv'] ??
|
source['lv'],
|
||||||
source['name'] ??
|
|
||||||
source['title'],
|
|
||||||
);
|
);
|
||||||
_dismissEndTime = _asInt(source['dismissEndTime']);
|
_dismissEndTime = _asInt(source['dismissEndTime']);
|
||||||
_dismissRemainSeconds = _asInt(source['dismissRemainSeconds']);
|
_dismissRemainSeconds = _asInt(source['dismissRemainSeconds']);
|
||||||
|
|||||||
@ -107,6 +107,12 @@ class SCRoomRocketStatusRes {
|
|||||||
_currentLevel = _intValue(
|
_currentLevel = _intValue(
|
||||||
json['currentLevel'] ??
|
json['currentLevel'] ??
|
||||||
json['current_level'] ??
|
json['current_level'] ??
|
||||||
|
json['currentRocketLevel'] ??
|
||||||
|
json['current_rocket_level'] ??
|
||||||
|
json['rocketCurrentLevel'] ??
|
||||||
|
json['rocket_current_level'] ??
|
||||||
|
json['levelNo'] ??
|
||||||
|
json['level_no'] ??
|
||||||
json['toLevel'] ??
|
json['toLevel'] ??
|
||||||
json['to_level'] ??
|
json['to_level'] ??
|
||||||
json['nextLevel'] ??
|
json['nextLevel'] ??
|
||||||
@ -120,6 +126,12 @@ class SCRoomRocketStatusRes {
|
|||||||
_level =
|
_level =
|
||||||
json['level'] ??
|
json['level'] ??
|
||||||
json['rocket_level'] ??
|
json['rocket_level'] ??
|
||||||
|
json['currentRocketLevel'] ??
|
||||||
|
json['current_rocket_level'] ??
|
||||||
|
json['rocketCurrentLevel'] ??
|
||||||
|
json['rocket_current_level'] ??
|
||||||
|
json['levelNo'] ??
|
||||||
|
json['level_no'] ??
|
||||||
json['toLevel'] ??
|
json['toLevel'] ??
|
||||||
json['to_level'] ??
|
json['to_level'] ??
|
||||||
json['nextLevel'] ??
|
json['nextLevel'] ??
|
||||||
|
|||||||
@ -60,7 +60,11 @@ abstract class SocialChatRoomRepository {
|
|||||||
Future micGoDown(String roomId, num mickIndex);
|
Future micGoDown(String roomId, num mickIndex);
|
||||||
|
|
||||||
///这个用户是否可以被踢下麦
|
///这个用户是否可以被踢下麦
|
||||||
Future<bool> kickOffMicrophone(String roomId, String kickUserId);
|
Future<bool> kickOffMicrophone(
|
||||||
|
String roomId,
|
||||||
|
String kickUserId, {
|
||||||
|
num? mickIndex,
|
||||||
|
});
|
||||||
|
|
||||||
///锁麦
|
///锁麦
|
||||||
Future micLock(String roomId, num mickIndex, bool lock);
|
Future micLock(String roomId, num mickIndex, bool lock);
|
||||||
|
|||||||
@ -952,6 +952,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
final result = await http.post<bool>(
|
final result = await http.post<bool>(
|
||||||
"a4465894e21be08d833ef701566ffe1b6a835338046b35fb94271445c996f6e3574aff257ce7e668d08f4caccd1c6232",
|
"a4465894e21be08d833ef701566ffe1b6a835338046b35fb94271445c996f6e3574aff257ce7e668d08f4caccd1c6232",
|
||||||
data: data,
|
data: data,
|
||||||
|
extra: const {BaseNetworkClient.silentErrorToastKey: true},
|
||||||
fromJson: (json) => json as bool,
|
fromJson: (json) => json as bool,
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -22,6 +22,9 @@ String? scCpRelationLevelTextFromCabin(
|
|||||||
if (computedText != null) {
|
if (computedText != null) {
|
||||||
return computedText;
|
return computedText;
|
||||||
}
|
}
|
||||||
|
if (currentValue != null && currentValue <= 0) {
|
||||||
|
return 'Lv.0';
|
||||||
|
}
|
||||||
|
|
||||||
final currentConfig = _CpRelationLevelSource.fromDynamic(
|
final currentConfig = _CpRelationLevelSource.fromDynamic(
|
||||||
cabin.currentCpCabinConfigCO,
|
cabin.currentCpCabinConfigCO,
|
||||||
@ -51,7 +54,13 @@ String? scCpRelationLevelTextFromRightsConfig(
|
|||||||
level: computedLevel?.level,
|
level: computedLevel?.level,
|
||||||
name: computedLevel?.levelName,
|
name: computedLevel?.levelName,
|
||||||
);
|
);
|
||||||
return computedText ?? _blankAsNull(fallbackText);
|
if (computedText != null) {
|
||||||
|
return computedText;
|
||||||
|
}
|
||||||
|
if (currentValue != null && currentValue <= 0) {
|
||||||
|
return 'Lv.0';
|
||||||
|
}
|
||||||
|
return _blankAsNull(fallbackText);
|
||||||
}
|
}
|
||||||
|
|
||||||
String scCpRelationLevelDisplayText(String? rawText) {
|
String scCpRelationLevelDisplayText(String? rawText) {
|
||||||
@ -86,9 +95,9 @@ int? scCpRelationLevelIndexOrNull(String? levelText) {
|
|||||||
r'(?:Lv\.?|Level|等级|级)\s*([0-5])|([0-5])\s*(?:级|Level)',
|
r'(?:Lv\.?|Level|等级|级)\s*([0-5])|([0-5])\s*(?:级|Level)',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
).firstMatch(text);
|
).firstMatch(text);
|
||||||
final fallbackMatch = RegExp(r'[0-5]').firstMatch(text);
|
final compactMatch = RegExp(r'^[0-5]$').firstMatch(text);
|
||||||
final rawLevel =
|
final rawLevel =
|
||||||
levelMatch?.group(1) ?? levelMatch?.group(2) ?? fallbackMatch?.group(0);
|
levelMatch?.group(1) ?? levelMatch?.group(2) ?? compactMatch?.group(0);
|
||||||
final level = int.tryParse(rawLevel ?? '');
|
final level = int.tryParse(rawLevel ?? '');
|
||||||
return level?.clamp(0, 5).toInt();
|
return level?.clamp(0, 5).toInt();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,36 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_dialog.dart';
|
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_dialog.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog.dart';
|
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
group('room rocket reward popup audience', () {
|
||||||
|
test('winner ids do not block empty reward users', () {
|
||||||
|
final shouldShow = roomRocketRewardPopupTargetsCurrentUser(
|
||||||
|
payload: {
|
||||||
|
'winnerUserIds': ['user-a', 'user-b'],
|
||||||
|
},
|
||||||
|
currentUserKeys: {'user-c'},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(shouldShow, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('explicit popup ids still target popup recipients', () {
|
||||||
|
final shouldShow = roomRocketRewardPopupTargetsCurrentUser(
|
||||||
|
payload: {
|
||||||
|
'popupUserIds': ['user-a', 'user-b'],
|
||||||
|
},
|
||||||
|
currentUserKeys: {'user-c'},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(shouldShow, isFalse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('room rocket reward dialog loader', () {
|
group('room rocket reward dialog loader', () {
|
||||||
test('deduplicates reward records by id', () {
|
test('deduplicates reward records by id', () {
|
||||||
final records = debugUniqueRoomRocketRewardRecords([
|
final records = debugUniqueRoomRocketRewardRecords([
|
||||||
|
|||||||
@ -264,6 +264,17 @@ void main() {
|
|||||||
expect(status.displayPercent, 0);
|
expect(status.displayPercent, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parses realtime current rocket level aliases', () {
|
||||||
|
final status = SCRoomRocketStatusRes.fromJson({
|
||||||
|
'room_id': 'room-1',
|
||||||
|
'current_rocket_level': 2,
|
||||||
|
'display_percent': 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status.currentLevel, 2);
|
||||||
|
expect(status.level, 2);
|
||||||
|
});
|
||||||
|
|
||||||
test('merges realtime rocket status without dropping level assets', () {
|
test('merges realtime rocket status without dropping level assets', () {
|
||||||
final current = SCRoomRocketStatusRes.fromJson({
|
final current = SCRoomRocketStatusRes.fromJson({
|
||||||
'room_id': 'room-1',
|
'room_id': 'room-1',
|
||||||
|
|||||||
@ -95,6 +95,37 @@ void main() {
|
|||||||
|
|
||||||
expect(scCpRelationTypeBetweenProfiles(left, right), isNull);
|
expect(scCpRelationTypeBetweenProfiles(left, right), isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('relation version changes when cp level or value changes', () {
|
||||||
|
final base = SocialChatUserProfile(
|
||||||
|
id: '100',
|
||||||
|
cpList: [
|
||||||
|
CPRes(
|
||||||
|
meUserId: '100',
|
||||||
|
cpUserId: '200',
|
||||||
|
relationType: 'CP',
|
||||||
|
levelText: 'Lv.1',
|
||||||
|
cpValue: 90,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
final upgraded = base.copyWith(
|
||||||
|
cpList: [
|
||||||
|
CPRes(
|
||||||
|
meUserId: '100',
|
||||||
|
cpUserId: '200',
|
||||||
|
relationType: 'CP',
|
||||||
|
levelText: 'Lv.2',
|
||||||
|
cpValue: 120,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
scCpProfileRelationVersion(base),
|
||||||
|
isNot(scCpProfileRelationVersion(upgraded)),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('scCpRelationLevelTextFromCabin', () {
|
group('scCpRelationLevelTextFromCabin', () {
|
||||||
@ -112,6 +143,15 @@ void main() {
|
|||||||
expect(scCpRelationLevelTextFromCabin(cabin), 'Lv.2 Sweet Love');
|
expect(scCpRelationLevelTextFromCabin(cabin), 'Lv.2 Sweet Love');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('keeps zero value relation at level zero', () {
|
||||||
|
final cabin = SCCpCabinRes(
|
||||||
|
cpVal: 0,
|
||||||
|
currentCpCabinConfigCO: const {'level': 5, 'levelName': 'Soulmate'},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(scCpRelationLevelTextFromCabin(cabin), 'Lv.0');
|
||||||
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'supports zero based CP levels and hides hardcoded simple love copy',
|
'supports zero based CP levels and hides hardcoded simple love copy',
|
||||||
() {
|
() {
|
||||||
@ -140,5 +180,22 @@ void main() {
|
|||||||
expect(scCpRelationLevelTextFromCabin(levelOneCabin), 'Lv.1');
|
expect(scCpRelationLevelTextFromCabin(levelOneCabin), 'Lv.1');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('does not infer cp level from unrelated digits', () {
|
||||||
|
expect(scCpRelationLevelIndexOrNull('520 Couple'), isNull);
|
||||||
|
expect(scCpRelationLevelIndexOrNull('Lv.5 Soulmate'), 5);
|
||||||
|
expect(scCpRelationLevelIndexOrNull('5'), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not parse generic relation name as level text', () {
|
||||||
|
final relation = CPRes.fromJson({
|
||||||
|
'meUserId': '100',
|
||||||
|
'cpUserId': '200',
|
||||||
|
'relationType': 'CP',
|
||||||
|
'name': '520 Couple',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(relation.levelText, isNull);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user