Merge branch 'feature' of gitea.haiyihy.com:hy/chatapp3-flutter into feature
This commit is contained in:
commit
faaeeded32
@ -724,7 +724,7 @@
|
||||
"roomRocketNoteTitle": "Note",
|
||||
"roomRocketNoteSectionTitle": "Each time a VIP",
|
||||
"roomRocketNoteSectionBody": "1. Each time a VIP level is purchased, it is valid for 30 days, and can be manually renewed after expiration.",
|
||||
"roomRocketRewardEmptyText": "No rewards earned. Better luck next time.",
|
||||
"roomRocketRewardEmptyText": "No reward",
|
||||
"roomRocketRecordEmptyText": "No reward records",
|
||||
"launchRocket": "Launch Rocket",
|
||||
"roomRocketFuelFullNotice": "Rocket fuel is full! Launching in {1}s!"
|
||||
|
||||
@ -560,7 +560,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
||||
///礼物上飘动画
|
||||
_floatingGiftListener(Msg msg) {
|
||||
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||||
rtcProvider.scheduleRoomRocketStatusRefreshForGift();
|
||||
if (!rtcProvider.shouldShowRoomVisualEffects) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -305,6 +305,12 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
Duration(seconds: 5),
|
||||
Duration(seconds: 10),
|
||||
];
|
||||
static const List<Duration> _roomRocketEntryReadyFallbackDelays = [
|
||||
Duration(milliseconds: 300),
|
||||
Duration(milliseconds: 900),
|
||||
Duration(milliseconds: 1800),
|
||||
Duration(milliseconds: 3200),
|
||||
];
|
||||
static const int _roomPasswordNotTrueErrorCode = 9005;
|
||||
|
||||
static const Duration _micListPollingInterval = Duration(seconds: 2);
|
||||
@ -347,6 +353,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
ValueNotifier<String?>? _roomRocketLaunchTopAvatarNotifier;
|
||||
Timer? _roomRocketGiftRefreshTimer;
|
||||
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
||||
final List<Timer> _roomRocketEntryReadyFallbackTimers = [];
|
||||
Timer? _roomRedPacketPresenceTimer;
|
||||
Future<void>? _rtcEnginePrewarmTask;
|
||||
Future<void>? _pendingRoomSwitchRtcLeaveTask;
|
||||
@ -495,6 +502,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
_releaseRoomRocketLaunchTopAvatarNotifier();
|
||||
_roomRocketGiftRefreshTimer?.cancel();
|
||||
_roomRocketGiftRefreshTimer = null;
|
||||
_cancelRoomRocketEntryReadyFallbacks();
|
||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||
}
|
||||
_roomVisualEffectsEnabled = enabled;
|
||||
@ -506,7 +514,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
if (visible) {
|
||||
final roomId = (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||
if (roomId.isNotEmpty) {
|
||||
rtmProvider?.handleVoiceRoomReadyForRocketRewards(roomId);
|
||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2603,6 +2611,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
setRoomVisualEffectsEnabled(true);
|
||||
if (shouldOpenRoomPage) {
|
||||
VoiceRoomRoute.openVoiceRoom(context!);
|
||||
setVoiceRoomRouteVisible(true);
|
||||
}
|
||||
unawaited(
|
||||
_bootstrapEnteredVoiceRoomSession(
|
||||
@ -2703,7 +2712,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
roomId: roomId,
|
||||
);
|
||||
_loadRoomSecondaryData();
|
||||
rtmProvider?.handleVoiceRoomReadyForRocketRewards(roomId);
|
||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
||||
} catch (error, stackTrace) {
|
||||
if (!_isActiveRoomStartup(entryRequestSerial, roomId)) {
|
||||
return;
|
||||
@ -2825,6 +2834,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
roomRocketStatus = res;
|
||||
notifyListeners();
|
||||
_scheduleRoomRocketAssetPreload(roomId, res, afterRoomEntry: true);
|
||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
||||
})
|
||||
.catchError((e) {});
|
||||
_loadRoomRocketRewardPopups(roomId);
|
||||
@ -2947,6 +2957,42 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void _scheduleRoomRocketEntryReadyFallback(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
if (normalizedRoomId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
|
||||
for (final delay in _roomRocketEntryReadyFallbackDelays) {
|
||||
final timer = Timer(delay, () {
|
||||
_roomRocketEntryReadyFallbackTimers.removeWhere(
|
||||
(item) => !item.isActive,
|
||||
);
|
||||
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
|
||||
});
|
||||
_roomRocketEntryReadyFallbackTimers.add(timer);
|
||||
}
|
||||
}
|
||||
|
||||
void _notifyRoomRocketEntryReadyIfCurrent(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
final currentRoomId =
|
||||
currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? "";
|
||||
if (normalizedRoomId.isEmpty ||
|
||||
currentRoomId != normalizedRoomId ||
|
||||
!_voiceRoomRouteVisible) {
|
||||
return;
|
||||
}
|
||||
rtmProvider?.handleVoiceRoomReadyForRocketRewards(normalizedRoomId);
|
||||
}
|
||||
|
||||
void _cancelRoomRocketEntryReadyFallbacks() {
|
||||
for (final timer in _roomRocketEntryReadyFallbackTimers) {
|
||||
timer.cancel();
|
||||
}
|
||||
_roomRocketEntryReadyFallbackTimers.clear();
|
||||
}
|
||||
|
||||
void scheduleRoomRocketPostLaunchStatusRefresh({String? roomId}) {
|
||||
final resolvedRoomId =
|
||||
(roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||
@ -3482,9 +3528,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
final exitRtmProvider = rtmProvider;
|
||||
final navigationContext = context;
|
||||
try {
|
||||
exitRtmProvider?.msgAllListener = null;
|
||||
exitRtmProvider?.msgChatListener = null;
|
||||
exitRtmProvider?.msgGiftListener = null;
|
||||
SCGiftVapSvgaManager().stopPlayback();
|
||||
setRoomVisualEffectsEnabled(false);
|
||||
SCHeartbeatUtils.cancelTimer();
|
||||
@ -3520,9 +3563,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
||||
final isPreviewOnlyExit = _currentRoomIsEntryPreview;
|
||||
final exitRtmProvider = rtmProvider;
|
||||
try {
|
||||
exitRtmProvider?.msgAllListener = null;
|
||||
exitRtmProvider?.msgChatListener = null;
|
||||
exitRtmProvider?.msgGiftListener = null;
|
||||
SCGiftVapSvgaManager().stopPlayback();
|
||||
setRoomVisualEffectsEnabled(false);
|
||||
SCHeartbeatUtils.cancelTimer();
|
||||
|
||||
@ -135,6 +135,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
|
||||
static const int _roomRocketRewardPendingTtlMs = 120000;
|
||||
static const int _roomRocketRewardProbeTtlMs = 120000;
|
||||
static const int _roomRocketLaunchCountdownRetainMs = 10000;
|
||||
static const Duration _createRoomGroupTimeout = Duration(seconds: 12);
|
||||
static const List<Duration> _roomRocketRewardPopupRetryDelays = [
|
||||
Duration(milliseconds: 700),
|
||||
@ -262,6 +263,12 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
final _pendingRoomRocketRewardPopups =
|
||||
<String, _PendingRoomRocketRewardPopup>{};
|
||||
final Map<String, int> _recentRoomRocketRewardProbeTimes = <String, int>{};
|
||||
final Map<String, int> _roomRocketLaunchCountdownDueTimes = <String, int>{};
|
||||
final Map<String, String> _roomRocketLaunchCountdownKeysByRoom =
|
||||
<String, String>{};
|
||||
final _pendingRoomRocketCountdownLaunches =
|
||||
<String, RoomRocketLaunchBroadcastMessage>{};
|
||||
final Map<String, Timer> _roomRocketLaunchEffectTimers = <String, Timer>{};
|
||||
Timer? _roomRocketRewardPopupRetryTimer;
|
||||
Timer? _roomRocketRoomReadyRewardRetryTimer;
|
||||
int get currentLuckGiftBurstPlaybackToken => _luckGiftPushPlaybackToken;
|
||||
@ -1163,6 +1170,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
|
||||
addMsg(Msg msg) {
|
||||
_scheduleRoomRocketStatusRefreshForGiftMessage(msg);
|
||||
final mergedGiftMsg = _mergeGiftMessageIfNeeded(msg);
|
||||
if (mergedGiftMsg != null) {
|
||||
msgAllListener?.call(mergedGiftMsg);
|
||||
@ -1249,6 +1257,33 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void _scheduleRoomRocketStatusRefreshForGiftMessage(Msg msg) {
|
||||
if (msg.type != SCRoomMsgType.gift &&
|
||||
msg.type != SCRoomMsgType.luckGiftAnimOther) {
|
||||
return;
|
||||
}
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
return;
|
||||
}
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
);
|
||||
final currentRoomId =
|
||||
(rtcProvider.currenRoom?.roomProfile?.roomProfile?.id ?? '').trim();
|
||||
if (currentRoomId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final messageRoomId = msg.type == SCRoomMsgType.gift ? msg.msg?.trim() : '';
|
||||
if (messageRoomId != null &&
|
||||
messageRoomId.isNotEmpty &&
|
||||
messageRoomId != currentRoomId) {
|
||||
return;
|
||||
}
|
||||
rtcProvider.scheduleRoomRocketStatusRefreshForGift(roomId: currentRoomId);
|
||||
}
|
||||
|
||||
void _cacheRoomRedPacketMessage(Msg msg) {
|
||||
try {
|
||||
final decoded = jsonDecode(msg.msg ?? '');
|
||||
@ -1985,6 +2020,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
publishRegionBroadcast: true,
|
||||
);
|
||||
} else {
|
||||
_rememberRoomRocketLaunchCountdown(launch);
|
||||
_markRoomRocketRewardProbe(launch.roomId);
|
||||
_publishRoomRocketRegionBroadcastIfNeeded(launch);
|
||||
}
|
||||
@ -2002,6 +2038,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
if (!launch.isValid) {
|
||||
return;
|
||||
}
|
||||
_rememberRoomRocketLaunchCountdown(launch);
|
||||
|
||||
var isCurrentRoom = false;
|
||||
var isCurrentVisibleRoom = false;
|
||||
@ -2092,14 +2129,22 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
final pending = _pendingRoomRocketRewardPopups[normalizedRoomId];
|
||||
final hasProbe =
|
||||
_recentRoomRocketRewardProbeTimes[normalizedRoomId] != null;
|
||||
if (pending == null && !hasProbe) {
|
||||
final hasLaunch =
|
||||
_pendingRoomRocketCountdownLaunches[normalizedRoomId] != null;
|
||||
if (pending == null && !hasProbe && !hasLaunch) {
|
||||
return;
|
||||
}
|
||||
if (!_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
|
||||
_scheduleRoomRocketReadyRewardRetry(normalizedRoomId, attempt);
|
||||
return;
|
||||
}
|
||||
if (pending == null && attempt == 0) {
|
||||
final resumedLaunch = _resumeRoomRocketLaunchForCurrentRoom(
|
||||
normalizedRoomId,
|
||||
);
|
||||
if (pending == null && !hasProbe) {
|
||||
return;
|
||||
}
|
||||
if (!resumedLaunch && pending == null && attempt == 0) {
|
||||
_scheduleRoomRocketReadyRewardRetry(normalizedRoomId, attempt);
|
||||
return;
|
||||
}
|
||||
@ -2180,15 +2225,219 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
_recentRoomRocketRewardProbeTimes.clear();
|
||||
_roomRocketRoomReadyRewardRetryTimer?.cancel();
|
||||
_roomRocketRoomReadyRewardRetryTimer = null;
|
||||
_clearRoomRocketLaunchCountdownState();
|
||||
}
|
||||
|
||||
void _rememberRoomRocketLaunchCountdown(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
if (!launch.isValid) {
|
||||
return;
|
||||
}
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
_pruneRoomRocketLaunchCountdowns(now);
|
||||
final key = _roomRocketLaunchCountdownKey(launch);
|
||||
final existingDueAt = _roomRocketLaunchCountdownDueTimes[key];
|
||||
final dueAt =
|
||||
existingDueAt != null && existingDueAt > now
|
||||
? existingDueAt
|
||||
: now + _roomRocketLaunchCountdownDelay(launch).inMilliseconds;
|
||||
_roomRocketLaunchCountdownDueTimes[key] = dueAt;
|
||||
_roomRocketLaunchCountdownKeysByRoom[launch.roomId] = key;
|
||||
_pendingRoomRocketCountdownLaunches[launch.roomId] = launch;
|
||||
}
|
||||
|
||||
void _scheduleRoomRocketLaunchEffectsAfterCountdown(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
if (!launch.isValid) {
|
||||
return;
|
||||
}
|
||||
_rememberRoomRocketLaunchCountdown(launch);
|
||||
final key = _roomRocketLaunchCountdownKey(launch);
|
||||
final delay = _roomRocketLaunchCountdownRemaining(launch.roomId);
|
||||
final dueAt =
|
||||
_roomRocketLaunchCountdownDueTimes[key] ??
|
||||
DateTime.now().millisecondsSinceEpoch;
|
||||
_roomRocketLaunchEffectTimers[key]?.cancel();
|
||||
_roomRocketLaunchEffectTimers[key] = Timer(delay, () {
|
||||
_roomRocketLaunchEffectTimers.remove(key);
|
||||
if (_roomRocketLaunchCountdownDueTimes[key] != dueAt) {
|
||||
return;
|
||||
}
|
||||
_roomRocketLaunchCountdownDueTimes.remove(key);
|
||||
if (_roomRocketLaunchCountdownKeysByRoom[launch.roomId] == key) {
|
||||
_roomRocketLaunchCountdownKeysByRoom.remove(launch.roomId);
|
||||
}
|
||||
final pendingLaunch =
|
||||
_pendingRoomRocketCountdownLaunches.remove(launch.roomId) ?? launch;
|
||||
_playRoomRocketLaunchEffectsAndReward(pendingLaunch);
|
||||
});
|
||||
}
|
||||
|
||||
void _playRoomRocketLaunchEffectsAndReward(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
_clearRoomRocketLaunchCountdownForRoom(launch.roomId);
|
||||
if (!_isCurrentVisibleVoiceRoom(launch.roomId)) {
|
||||
_markRoomRocketRewardProbe(launch.roomId);
|
||||
return;
|
||||
}
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
_markRoomRocketRewardProbe(launch.roomId);
|
||||
return;
|
||||
}
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
);
|
||||
final shouldFetchReward =
|
||||
_pendingRoomRocketRewardPopups.containsKey(launch.roomId) ||
|
||||
_recentRoomRocketRewardProbeTimes.containsKey(launch.roomId);
|
||||
final pending = _pendingRoomRocketRewardPopups.remove(launch.roomId);
|
||||
_recentRoomRocketRewardProbeTimes.remove(launch.roomId);
|
||||
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
||||
if (shouldFetchReward) {
|
||||
unawaited(
|
||||
_showRoomRocketRewardPopupDialog(
|
||||
launch.roomId,
|
||||
initialRoomRecords:
|
||||
pending?.initialRoomRecords ??
|
||||
const <SCRoomRocketRewardRecordRes>[],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _resumeRoomRocketLaunchForCurrentRoom(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
final launch = _pendingRoomRocketCountdownLaunches[normalizedRoomId];
|
||||
if (launch == null || !_isCurrentVisibleVoiceRoom(normalizedRoomId)) {
|
||||
return false;
|
||||
}
|
||||
final currentContext = context;
|
||||
if (currentContext == null || !currentContext.mounted) {
|
||||
return false;
|
||||
}
|
||||
final rtcProvider = Provider.of<RealTimeCommunicationManager>(
|
||||
currentContext,
|
||||
listen: false,
|
||||
);
|
||||
final remaining = _roomRocketLaunchCountdownRemaining(normalizedRoomId);
|
||||
if (remaining > Duration.zero) {
|
||||
_addRoomRocketLaunchNoticeIfNeeded(
|
||||
rtcProvider: rtcProvider,
|
||||
launch: launch,
|
||||
secondsOverride: _roomRocketRemainingSeconds(remaining),
|
||||
);
|
||||
_scheduleRoomRocketLaunchEffectsAfterCountdown(launch);
|
||||
} else {
|
||||
_playRoomRocketLaunchEffectsAndReward(launch);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int _roomRocketRemainingSeconds(Duration duration) {
|
||||
final seconds = (duration.inMilliseconds / 1000).ceil();
|
||||
return seconds.clamp(1, 60).toInt();
|
||||
}
|
||||
|
||||
Duration _roomRocketLaunchCountdownRemaining(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
if (normalizedRoomId.isEmpty) {
|
||||
return Duration.zero;
|
||||
}
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
_pruneRoomRocketLaunchCountdowns(now);
|
||||
final key = _roomRocketLaunchCountdownKeysByRoom[normalizedRoomId];
|
||||
final dueAt = key == null ? null : _roomRocketLaunchCountdownDueTimes[key];
|
||||
if (dueAt == null || dueAt <= now) {
|
||||
return Duration.zero;
|
||||
}
|
||||
return Duration(milliseconds: dueAt - now);
|
||||
}
|
||||
|
||||
Duration _roomRocketLaunchCountdownDelay(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
final seconds = launch.durationSeconds <= 0 ? 10 : launch.durationSeconds;
|
||||
return Duration(seconds: seconds.clamp(0, 60).toInt());
|
||||
}
|
||||
|
||||
String _roomRocketLaunchCountdownKey(
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
final launchKey = _roomRocketLaunchNoticeKey(launch);
|
||||
if (launchKey.isNotEmpty) {
|
||||
return launchKey;
|
||||
}
|
||||
return 'rocket_launch_countdown|${launch.roomId}';
|
||||
}
|
||||
|
||||
void _pruneRoomRocketLaunchCountdowns(int now) {
|
||||
final expiredKeys = <String>{};
|
||||
_roomRocketLaunchCountdownDueTimes.removeWhere((key, dueAt) {
|
||||
final expired = now - dueAt > _roomRocketLaunchCountdownRetainMs;
|
||||
if (expired) {
|
||||
expiredKeys.add(key);
|
||||
}
|
||||
return expired;
|
||||
});
|
||||
if (expiredKeys.isEmpty) {
|
||||
return;
|
||||
}
|
||||
_roomRocketLaunchCountdownKeysByRoom.removeWhere(
|
||||
(_, key) => expiredKeys.contains(key),
|
||||
);
|
||||
_pendingRoomRocketCountdownLaunches.removeWhere(
|
||||
(roomId, _) => !_roomRocketLaunchCountdownKeysByRoom.containsKey(roomId),
|
||||
);
|
||||
}
|
||||
|
||||
void _clearRoomRocketLaunchCountdownState() {
|
||||
_roomRocketLaunchCountdownDueTimes.clear();
|
||||
_roomRocketLaunchCountdownKeysByRoom.clear();
|
||||
_pendingRoomRocketCountdownLaunches.clear();
|
||||
for (final timer in _roomRocketLaunchEffectTimers.values) {
|
||||
timer.cancel();
|
||||
}
|
||||
_roomRocketLaunchEffectTimers.clear();
|
||||
}
|
||||
|
||||
void _clearRoomRocketLaunchCountdownForRoom(String roomId) {
|
||||
final normalizedRoomId = roomId.trim();
|
||||
final key = _roomRocketLaunchCountdownKeysByRoom.remove(normalizedRoomId);
|
||||
if (key != null) {
|
||||
_roomRocketLaunchCountdownDueTimes.remove(key);
|
||||
_roomRocketLaunchEffectTimers.remove(key)?.cancel();
|
||||
}
|
||||
_pendingRoomRocketCountdownLaunches.remove(normalizedRoomId);
|
||||
}
|
||||
|
||||
void _handleCurrentVisibleRoomRocketLaunch({
|
||||
required RealTimeCommunicationManager rtcProvider,
|
||||
required RoomRocketLaunchBroadcastMessage launch,
|
||||
bool publishRegionBroadcast = false,
|
||||
}) {
|
||||
if (_addRoomRocketLaunchNoticeIfNeeded(
|
||||
rtcProvider: rtcProvider,
|
||||
launch: launch,
|
||||
publishRegionBroadcast: publishRegionBroadcast,
|
||||
)) {
|
||||
_markRoomRocketRewardProbe(launch.roomId);
|
||||
_scheduleRoomRocketLaunchEffectsAfterCountdown(launch);
|
||||
}
|
||||
}
|
||||
|
||||
bool _addRoomRocketLaunchNoticeIfNeeded({
|
||||
required RealTimeCommunicationManager rtcProvider,
|
||||
required RoomRocketLaunchBroadcastMessage launch,
|
||||
bool publishRegionBroadcast = false,
|
||||
int? secondsOverride,
|
||||
}) {
|
||||
if (!_isCurrentVisibleVoiceRoom(launch.roomId)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
final launchKey = _roomRocketLaunchNoticeKey(launch);
|
||||
final looseLaunchKey = _roomRocketLaunchLooseKey(launch);
|
||||
@ -2199,26 +2448,27 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
_recentRoomRocketLaunchTimes.containsKey(launchKey)) ||
|
||||
(looseLaunchKey.isNotEmpty &&
|
||||
_recentRoomRocketLaunchLooseTimes.containsKey(looseLaunchKey));
|
||||
if (!alreadyHandled && launchKey.isNotEmpty) {
|
||||
_recentRoomRocketLaunchTimes[launchKey] = now;
|
||||
if (looseLaunchKey.isNotEmpty) {
|
||||
_recentRoomRocketLaunchLooseTimes[looseLaunchKey] = now;
|
||||
}
|
||||
addMsg(
|
||||
_buildRoomRocketLaunchNoticeMsg(
|
||||
_firstNonBlank([
|
||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount,
|
||||
launch.roomAccount,
|
||||
]),
|
||||
launch,
|
||||
),
|
||||
);
|
||||
if (publishRegionBroadcast) {
|
||||
_publishRoomRocketRegionBroadcastIfNeeded(launch);
|
||||
}
|
||||
rtcProvider.handleRoomRocketLaunchBroadcast(launch);
|
||||
if (alreadyHandled || launchKey.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
unawaited(_showRoomRocketRewardPopupDialog(launch.roomId));
|
||||
_recentRoomRocketLaunchTimes[launchKey] = now;
|
||||
if (looseLaunchKey.isNotEmpty) {
|
||||
_recentRoomRocketLaunchLooseTimes[looseLaunchKey] = now;
|
||||
}
|
||||
addMsg(
|
||||
_buildRoomRocketLaunchNoticeMsg(
|
||||
_firstNonBlank([
|
||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.roomAccount,
|
||||
launch.roomAccount,
|
||||
]),
|
||||
launch,
|
||||
secondsOverride: secondsOverride,
|
||||
),
|
||||
);
|
||||
if (publishRegionBroadcast) {
|
||||
_publishRoomRocketRegionBroadcastIfNeeded(launch);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void _publishRoomRocketRegionBroadcastIfNeeded(
|
||||
@ -2294,7 +2544,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
if (!_isCurrentVisibleVoiceRoom(roomId)) {
|
||||
return;
|
||||
}
|
||||
if (res.records.isEmpty && initialRoomRecords.isEmpty) {
|
||||
if (res.records.isEmpty &&
|
||||
initialRoomRecords.isEmpty &&
|
||||
attempt < _roomRocketRewardPopupRetryDelays.length) {
|
||||
_scheduleRoomRocketRewardPopupRetry(
|
||||
roomId,
|
||||
attempt,
|
||||
@ -2453,9 +2705,12 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
||||
|
||||
Msg _buildRoomRocketLaunchNoticeMsg(
|
||||
String groupId,
|
||||
RoomRocketLaunchBroadcastMessage launch,
|
||||
) {
|
||||
final seconds = launch.durationSeconds <= 0 ? 10 : launch.durationSeconds;
|
||||
RoomRocketLaunchBroadcastMessage launch, {
|
||||
int? secondsOverride,
|
||||
}) {
|
||||
final seconds =
|
||||
secondsOverride ??
|
||||
(launch.durationSeconds <= 0 ? 10 : launch.durationSeconds);
|
||||
return Msg(
|
||||
groupId: groupId,
|
||||
msg: seconds.toString(),
|
||||
|
||||
@ -35,7 +35,7 @@ class RoomRocketRewardDialog extends StatelessWidget {
|
||||
final emptyText = _translate(
|
||||
localizations,
|
||||
'roomRocketRewardEmptyText',
|
||||
'No rewards earned. Better luck next time.',
|
||||
'No reward',
|
||||
);
|
||||
final contentDirection = Directionality.of(context);
|
||||
final resolvedRewards = _normalizeRewards(
|
||||
@ -257,7 +257,7 @@ class _RewardDialogCanvas extends StatelessWidget {
|
||||
if (isEmpty)
|
||||
_buildEmptyState()
|
||||
else ...[
|
||||
_buildRewardGrid(),
|
||||
rewards.isEmpty ? _buildRewardEmptyState() : _buildRewardGrid(),
|
||||
_buildWinnerList(),
|
||||
],
|
||||
_buildLoadingText(),
|
||||
@ -380,9 +380,6 @@ class _RewardDialogCanvas extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildRewardGrid() {
|
||||
if (rewards.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final columns = math.max(3, (rewards.length / 2).ceil());
|
||||
return Positioned(
|
||||
left: s(28),
|
||||
@ -425,6 +422,45 @@ class _RewardDialogCanvas extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRewardEmptyState() {
|
||||
return Positioned(
|
||||
left: s(0),
|
||||
top: s(268),
|
||||
width: s(_designWidth),
|
||||
height: s(178),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
_RewardDialogAssets.empty,
|
||||
width: s(72),
|
||||
height: s(72),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(height: s(10)),
|
||||
SizedBox(
|
||||
width: s(250),
|
||||
child: Text(
|
||||
emptyText,
|
||||
textDirection: contentDirection,
|
||||
textScaler: TextScaler.noScaling,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.72),
|
||||
fontSize: s(13),
|
||||
height: 1.25,
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWinnerList() {
|
||||
return Positioned(
|
||||
left: s(12),
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.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_reward_dialog.dart';
|
||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
||||
|
||||
void main() {
|
||||
@ -27,6 +30,30 @@ void main() {
|
||||
expect(records.map((item) => item.rewardAmount), [5000, 5200]);
|
||||
});
|
||||
});
|
||||
|
||||
group('room rocket reward dialog', () {
|
||||
testWidgets('shows reward empty state when room winners exist', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RoomRocketRewardDialog(
|
||||
rewards: <RoomRocketRewardItem>[],
|
||||
winners: <RoomRocketRewardWinner>[
|
||||
RoomRocketRewardWinner(nickname: 'test man'),
|
||||
],
|
||||
loadingText: '',
|
||||
useMockFallback: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('No reward'), findsOneWidget);
|
||||
expect(find.text('test man'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SCRoomRocketRewardRecordRes _rewardRecord({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user