修改bug
This commit is contained in:
parent
7e10b77d10
commit
5501345a7e
@ -16,7 +16,7 @@ class SCVariant1Config implements AppConfig {
|
|||||||
@override
|
@override
|
||||||
String get apiHost => const String.fromEnvironment(
|
String get apiHost => const String.fromEnvironment(
|
||||||
'API_HOST',
|
'API_HOST',
|
||||||
defaultValue: 'https://jvapi.haiyihy.com/',
|
defaultValue: 'http://192.168.110.64:1100/',
|
||||||
); // 默认连本地接口,本地调试可通过 --dart-define=API_HOST 覆盖,线上“https://jvapi.haiyihy.com/”
|
); // 默认连本地接口,本地调试可通过 --dart-define=API_HOST 覆盖,线上“https://jvapi.haiyihy.com/”
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -168,8 +168,7 @@ class SCGlobalConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String clampFloatingBroadcastScope(String value) {
|
static String clampFloatingBroadcastScope(String value) {
|
||||||
final scope = normalizeFloatingBroadcastScope(value);
|
return normalizeFloatingBroadcastScope(value);
|
||||||
return _isLowPerformanceDevice ? floatingBroadcastScopeOff : scope;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String floatingBroadcastScopeStorageKey(String? account) {
|
static String floatingBroadcastScopeStorageKey(String? account) {
|
||||||
@ -188,8 +187,7 @@ class SCGlobalConfig {
|
|||||||
|
|
||||||
static bool get isLowPerformanceDevice => _isLowPerformanceDevice;
|
static bool get isLowPerformanceDevice => _isLowPerformanceDevice;
|
||||||
|
|
||||||
static bool get allowsHighCostAnimations =>
|
static bool get allowsHighCostAnimations => true;
|
||||||
!_isLowPerformanceDevice && sdkInt > maxSdkNoAnim;
|
|
||||||
|
|
||||||
static int get recommendedImageCacheBytes =>
|
static int get recommendedImageCacheBytes =>
|
||||||
_isLowPerformanceDevice ? 40 * 1024 * 1024 : 100 * 1024 * 1024;
|
_isLowPerformanceDevice ? 40 * 1024 * 1024 : 100 * 1024 * 1024;
|
||||||
@ -211,19 +209,17 @@ class SCGlobalConfig {
|
|||||||
isLowRamDevice ||
|
isLowRamDevice ||
|
||||||
processorCount <= 6 ||
|
processorCount <= 6 ||
|
||||||
matchesKnownLowPerformanceModel;
|
matchesKnownLowPerformanceModel;
|
||||||
resetVisualEffectSwitchesToRecommendedDefaults();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool clampVisualEffectPreference(bool enabled) {
|
static bool clampVisualEffectPreference(bool enabled) {
|
||||||
return !_isLowPerformanceDevice && enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resetVisualEffectSwitchesToRecommendedDefaults() {
|
static void resetVisualEffectSwitchesToRecommendedDefaults() {
|
||||||
final enabled = !_isLowPerformanceDevice;
|
isEntryVehicleAnimation = true;
|
||||||
isEntryVehicleAnimation = enabled;
|
isGiftSpecialEffects = true;
|
||||||
isGiftSpecialEffects = enabled;
|
isFloatingAnimationInGlobal = true;
|
||||||
isFloatingAnimationInGlobal = enabled;
|
isLuckGiftSpecialEffects = true;
|
||||||
isLuckGiftSpecialEffects = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取当前业务逻辑策略
|
/// 获取当前业务逻辑策略
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import 'package:yumi/shared/tools/sc_message_utils.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_user_id_utils.dart';
|
import 'package:yumi/shared/tools/sc_user_id_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:marquee/marquee.dart';
|
import 'package:marquee/marquee.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -59,7 +58,6 @@ import 'package:yumi/shared/tools/sc_message_notifier.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
|
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/modules/index/main_route.dart';
|
import 'package:yumi/modules/index/main_route.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/sc_nine_patch_image.dart';
|
import 'package:yumi/ui_kit/widgets/sc_nine_patch_image.dart';
|
||||||
@ -81,29 +79,6 @@ const EdgeInsets _vipBubbleCenterSliceRatio = EdgeInsets.fromLTRB(
|
|||||||
);
|
);
|
||||||
const EdgeInsets _vipBubbleSourceEdgeInset = EdgeInsets.all(2);
|
const EdgeInsets _vipBubbleSourceEdgeInset = EdgeInsets.all(2);
|
||||||
|
|
||||||
String _resolveVipChatBubbleImageUrl(SCVipResourceRes? resource) {
|
|
||||||
final candidates = <String?>[
|
|
||||||
resource?.sourceResourceUrl,
|
|
||||||
resource?.previewUrl,
|
|
||||||
resource?.coverUrl,
|
|
||||||
resource?.cover,
|
|
||||||
];
|
|
||||||
return _firstNonBlankChatBubbleUrl([
|
|
||||||
...candidates.where((value) => SCNetworkSvgaWidget.isSvga(value)),
|
|
||||||
...candidates,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _firstNonBlankChatBubbleUrl(Iterable<String?> values) {
|
|
||||||
for (final value in values) {
|
|
||||||
final text = value?.trim();
|
|
||||||
if (text != null && text.isNotEmpty) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
class SCMessageChatPage extends StatefulWidget {
|
class SCMessageChatPage extends StatefulWidget {
|
||||||
final V2TimConversation? conversation;
|
final V2TimConversation? conversation;
|
||||||
final bool shrinkWrap;
|
final bool shrinkWrap;
|
||||||
@ -144,8 +119,6 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
///互相关注才能互相发送消息
|
///互相关注才能互相发送消息
|
||||||
bool canSendMsg = false;
|
bool canSendMsg = false;
|
||||||
SocialChatUserProfile? friend;
|
SocialChatUserProfile? friend;
|
||||||
SCVipResourceRes? _selfVipChatBubble;
|
|
||||||
bool _isSelfVipChatBubbleLoading = false;
|
|
||||||
final Map<String, _CpInviteMessageState> _localCpInviteStates = {};
|
final Map<String, _CpInviteMessageState> _localCpInviteStates = {};
|
||||||
|
|
||||||
///是否显示发送按钮
|
///是否显示发送按钮
|
||||||
@ -196,7 +169,6 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
rtmProvider?.onRevokeMessageListener = _onRevokeMessage;
|
rtmProvider?.onRevokeMessageListener = _onRevokeMessage;
|
||||||
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
|
rtmProvider?.onNewMessageCurrentConversationListener = _onNewMessage;
|
||||||
loadMsg();
|
loadMsg();
|
||||||
_loadSelfVipChatBubble();
|
|
||||||
|
|
||||||
if (_isC2CConversation && friend == null) {
|
if (_isC2CConversation && friend == null) {
|
||||||
loadFriend();
|
loadFriend();
|
||||||
@ -385,171 +357,6 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadSelfVipChatBubble() async {
|
|
||||||
if (_isSelfVipChatBubbleLoading) return;
|
|
||||||
final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
|
|
||||||
if (currentUserId == null || currentUserId.isEmpty) return;
|
|
||||||
|
|
||||||
_isSelfVipChatBubbleLoading = true;
|
|
||||||
final repository = SCVipRepositoryImp();
|
|
||||||
SCVipHomeRes? vipHome;
|
|
||||||
SCVipResourceRes? chatBubble;
|
|
||||||
|
|
||||||
try {
|
|
||||||
final home = await repository.vipHome();
|
|
||||||
vipHome = home;
|
|
||||||
chatBubble = _resolveSelfVipChatBubbleFromHome(home);
|
|
||||||
} catch (error) {}
|
|
||||||
|
|
||||||
if (chatBubble == null) {
|
|
||||||
try {
|
|
||||||
final status = await repository.vipStatus();
|
|
||||||
chatBubble = _resolveSelfVipChatBubbleFromStatus(status);
|
|
||||||
if (chatBubble == null &&
|
|
||||||
vipHome != null &&
|
|
||||||
_isUsableVipStatus(status)) {
|
|
||||||
chatBubble = _firstUsableVipChatBubble([
|
|
||||||
_currentVipLevelConfig(
|
|
||||||
vipHome,
|
|
||||||
targetLevel: status.levelInt,
|
|
||||||
targetLevelCode: status.levelCode,
|
|
||||||
)?.chatBubble,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
} catch (error) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
setState(() {
|
|
||||||
_selfVipChatBubble = chatBubble;
|
|
||||||
_isSelfVipChatBubbleLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SCVipResourceRes? _resolveSelfVipChatBubbleFromHome(SCVipHomeRes home) {
|
|
||||||
final status = home.state;
|
|
||||||
if (status?.active == false) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final config = _currentVipLevelConfig(home);
|
|
||||||
if (_isUsableVipStatus(status)) {
|
|
||||||
return _firstUsableVipChatBubble([
|
|
||||||
status?.chatBubble,
|
|
||||||
config?.chatBubble,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
final profileLevel =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.vipLevelForColoredId ??
|
|
||||||
0;
|
|
||||||
if (profileLevel > 0) {
|
|
||||||
return _firstUsableVipChatBubble([config?.chatBubble]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCVipResourceRes? _resolveSelfVipChatBubbleFromStatus(SCVipStatusRes status) {
|
|
||||||
if (!_isUsableVipStatus(status)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return _firstUsableVipChatBubble([status.chatBubble]);
|
|
||||||
}
|
|
||||||
|
|
||||||
SCVipLevelConfigRes? _currentVipLevelConfig(
|
|
||||||
SCVipHomeRes home, {
|
|
||||||
int? targetLevel,
|
|
||||||
String? targetLevelCode,
|
|
||||||
}) {
|
|
||||||
final status = home.state;
|
|
||||||
final statusLevel = _vipLevelIntFromStatus(status);
|
|
||||||
final profileLevel =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.vipLevelForColoredId ??
|
|
||||||
0;
|
|
||||||
final resolvedTargetLevel =
|
|
||||||
(targetLevel ?? 0) > 0
|
|
||||||
? targetLevel!
|
|
||||||
: (statusLevel > 0 ? statusLevel : profileLevel);
|
|
||||||
final statusLevelCode =
|
|
||||||
targetLevelCode?.trim() ?? status?.levelCode?.trim();
|
|
||||||
|
|
||||||
for (final level in home.levels) {
|
|
||||||
if (resolvedTargetLevel > 0 && level.levelInt == resolvedTargetLevel) {
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (statusLevelCode != null && statusLevelCode.isNotEmpty) {
|
|
||||||
for (final level in home.levels) {
|
|
||||||
if (_sameVipLevelCode(level.levelCode, statusLevelCode)) {
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isUsableVipStatus(SCVipStatusRes? status) {
|
|
||||||
return status != null &&
|
|
||||||
status.active != false &&
|
|
||||||
(_vipLevelIntFromStatus(status) > 0 ||
|
|
||||||
_hasNonBlankText(status.levelCode) ||
|
|
||||||
_hasNonBlankText(status.displayName) ||
|
|
||||||
_resolveVipChatBubbleImageUrl(status.chatBubble).isNotEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _vipLevelIntFromStatus(SCVipStatusRes? status) {
|
|
||||||
final explicitLevel = status?.levelInt ?? 0;
|
|
||||||
if (explicitLevel > 0) {
|
|
||||||
return explicitLevel;
|
|
||||||
}
|
|
||||||
return _parseVipLevelText(status?.levelCode) ??
|
|
||||||
_parseVipLevelText(status?.displayName) ??
|
|
||||||
0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int? _parseVipLevelText(String? value) {
|
|
||||||
final text = value?.trim();
|
|
||||||
if (text == null || text.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final match = RegExp(
|
|
||||||
r'(?:S?VIP)?\s*([1-9]\d*)',
|
|
||||||
caseSensitive: false,
|
|
||||||
).firstMatch(text);
|
|
||||||
return int.tryParse(match?.group(1) ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _sameVipLevelCode(String? left, String? right) {
|
|
||||||
final normalizedLeft = left?.replaceAll(RegExp(r'\s+'), '').toLowerCase();
|
|
||||||
final normalizedRight = right?.replaceAll(RegExp(r'\s+'), '').toLowerCase();
|
|
||||||
return normalizedLeft != null &&
|
|
||||||
normalizedLeft.isNotEmpty &&
|
|
||||||
normalizedLeft == normalizedRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _hasNonBlankText(String? value) {
|
|
||||||
return value?.trim().isNotEmpty ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCVipResourceRes? _firstUsableVipChatBubble(
|
|
||||||
Iterable<SCVipResourceRes?> resources,
|
|
||||||
) {
|
|
||||||
for (final resource in resources) {
|
|
||||||
if (_resolveVipChatBubbleImageUrl(resource).isNotEmpty) {
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _vipChatBubbleLogValue(SCVipResourceRes? resource) {
|
|
||||||
if (resource == null) return 'null';
|
|
||||||
return '{id:${resource.resourceId},name:${resource.name},'
|
|
||||||
'previewUrl:${resource.previewUrl},'
|
|
||||||
'sourceUrl:${resource.sourceResourceUrl},'
|
|
||||||
'selectedUrl:${_resolveVipChatBubbleImageUrl(resource)}}';
|
|
||||||
}
|
|
||||||
|
|
||||||
///消息列表
|
///消息列表
|
||||||
Widget _msgList() {
|
Widget _msgList() {
|
||||||
return SmartRefresher(
|
return SmartRefresher(
|
||||||
@ -595,7 +402,6 @@ class _SCMessageChatPageState extends State<SCMessageChatPage> {
|
|||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(c, i) => _MessageItem(
|
(c, i) => _MessageItem(
|
||||||
message: currentConversationMessageList[i],
|
message: currentConversationMessageList[i],
|
||||||
selfVipChatBubble: _selfVipChatBubble,
|
|
||||||
currentConversation: currentConversation,
|
currentConversation: currentConversation,
|
||||||
preMessage:
|
preMessage:
|
||||||
i > 0 ? currentConversationMessageList[i - 1] : null,
|
i > 0 ? currentConversationMessageList[i - 1] : null,
|
||||||
@ -1182,7 +988,6 @@ class _MessageItem extends StatelessWidget {
|
|||||||
|
|
||||||
final V2TimMessage message;
|
final V2TimMessage message;
|
||||||
final V2TimMessage? preMessage;
|
final V2TimMessage? preMessage;
|
||||||
final SCVipResourceRes? selfVipChatBubble;
|
|
||||||
final V2TimConversation? currentConversation;
|
final V2TimConversation? currentConversation;
|
||||||
|
|
||||||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||||||
@ -1201,7 +1006,6 @@ class _MessageItem extends StatelessWidget {
|
|||||||
|
|
||||||
_MessageItem({
|
_MessageItem({
|
||||||
required this.message,
|
required this.message,
|
||||||
this.selfVipChatBubble,
|
|
||||||
this.currentConversation,
|
this.currentConversation,
|
||||||
this.preMessage,
|
this.preMessage,
|
||||||
// this.userProfile,
|
// this.userProfile,
|
||||||
@ -2827,24 +2631,18 @@ class _MessageItem extends StatelessWidget {
|
|||||||
if (chatBubbleUrl.isNotEmpty) {
|
if (chatBubbleUrl.isNotEmpty) {
|
||||||
return _buildVipTextBubble(content: content, imageUrl: chatBubbleUrl);
|
return _buildVipTextBubble(content: content, imageUrl: chatBubbleUrl);
|
||||||
}
|
}
|
||||||
if (message.isSelf ?? false) {
|
|
||||||
final vipChatBubbleUrl = _resolveVipChatBubbleImageUrl(selfVipChatBubble);
|
|
||||||
if (vipChatBubbleUrl.isNotEmpty) {
|
|
||||||
return _buildVipTextBubble(
|
|
||||||
content: content,
|
|
||||||
imageUrl: vipChatBubbleUrl,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _buildDefaultTextBubble(content);
|
return _buildDefaultTextBubble(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropsResources? _currentUserChatBubble() {
|
PropsResources? _currentUserChatBubble() {
|
||||||
final memoryBubble =
|
final currentUser = AccountStorage().getCurrentUser();
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.getChatBox();
|
final memoryBubble = currentUser?.userProfile?.getChatBox();
|
||||||
if (_resolveChatBubbleImageUrl(memoryBubble).isNotEmpty) {
|
if (_resolveChatBubbleImageUrl(memoryBubble).isNotEmpty) {
|
||||||
return memoryBubble;
|
return memoryBubble;
|
||||||
}
|
}
|
||||||
|
if (currentUser?.userProfile != null) {
|
||||||
|
return memoryBubble;
|
||||||
|
}
|
||||||
|
|
||||||
final userJson = DataPersistence.getCurrentUser();
|
final userJson = DataPersistence.getCurrentUser();
|
||||||
if (userJson.isEmpty) {
|
if (userJson.isEmpty) {
|
||||||
|
|||||||
@ -1900,16 +1900,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
'animationCount=$animationCount',
|
'animationCount=$animationCount',
|
||||||
);
|
);
|
||||||
SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
|
SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
|
||||||
} else if (!_isCpGift(gift) &&
|
|
||||||
SCGlobalConfig.isLowPerformanceDevice &&
|
|
||||||
SCGlobalConfig.isGiftSpecialEffects &&
|
|
||||||
(rtcProvider?.shouldShowRoomVisualEffects ?? false)) {
|
|
||||||
_enqueueLowPerformanceGiftCompensation(
|
|
||||||
gift: gift,
|
|
||||||
receiver: u.user,
|
|
||||||
quantity: quantity,
|
|
||||||
coins: coins,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'skip local play because visual effects disabled '
|
'skip local play because visual effects disabled '
|
||||||
@ -1936,52 +1926,10 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _enqueueLowPerformanceGiftCompensation({
|
|
||||||
required SocialChatGiftRes gift,
|
|
||||||
required SocialChatUserProfile? receiver,
|
|
||||||
required int quantity,
|
|
||||||
required num coins,
|
|
||||||
}) {
|
|
||||||
final sender = AccountStorage().getCurrentUser()?.userProfile;
|
|
||||||
final roomId = rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
|
||||||
_enqueueGiftFloatingMessage(
|
|
||||||
SCFloatingMessage(
|
|
||||||
type: 1,
|
|
||||||
priority: 900,
|
|
||||||
userId: sender?.id ?? "",
|
|
||||||
toUserId: receiver?.id ?? "",
|
|
||||||
userAvatarUrl: sender?.userAvatar ?? "",
|
|
||||||
toUserAvatarUrl: receiver?.userAvatar ?? "",
|
|
||||||
userName: sender?.userNickname ?? "",
|
|
||||||
toUserName: receiver?.userNickname ?? "",
|
|
||||||
giftUrl: gift.giftPhoto ?? "",
|
|
||||||
giftId: gift.id,
|
|
||||||
giftTab: _floatingGiftTabFor(gift),
|
|
||||||
number: quantity,
|
|
||||||
coins: coins,
|
|
||||||
roomId: roomId,
|
|
||||||
),
|
|
||||||
dedupKey: _giftFloatingDedupKey(
|
|
||||||
roomId: roomId,
|
|
||||||
senderId: sender?.id,
|
|
||||||
receiverId: receiver?.id,
|
|
||||||
giftId: gift.id,
|
|
||||||
quantity: quantity,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _enqueueGiftFloatingMessage(
|
void _enqueueGiftFloatingMessage(
|
||||||
SCFloatingMessage message, {
|
SCFloatingMessage message, {
|
||||||
String? dedupKey,
|
String? dedupKey,
|
||||||
}) {
|
}) {
|
||||||
if (SCGlobalConfig.isLowPerformanceDevice) {
|
|
||||||
OverlayManager().addLowPerformanceCompensationMessage(
|
|
||||||
message,
|
|
||||||
dedupKey: dedupKey,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OverlayManager().addMessage(message);
|
OverlayManager().addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,24 +6,24 @@ class BaishunLoadingView extends StatelessWidget {
|
|||||||
const BaishunLoadingView({
|
const BaishunLoadingView({
|
||||||
super.key,
|
super.key,
|
||||||
this.message = 'Loading game...',
|
this.message = 'Loading game...',
|
||||||
|
this.backgroundColor = const Color(0xFF081915),
|
||||||
});
|
});
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
|
final Color backgroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black.withValues(alpha: 0.42),
|
color: backgroundColor,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 22.w, vertical: 18.w),
|
padding: EdgeInsets.symmetric(horizontal: 22.w, vertical: 18.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF102D28).withValues(alpha: 0.92),
|
color: const Color(0xFF102D28).withValues(alpha: 0.92),
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(
|
border: Border.all(color: Colors.white.withValues(alpha: 0.1)),
|
||||||
color: Colors.white.withValues(alpha: 0.1),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|||||||
@ -295,6 +295,9 @@ class _BagsChatboxPageState
|
|||||||
res.propsResources?.id ?? "",
|
res.propsResources?.id ?? "",
|
||||||
unload,
|
unload,
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
await profileManager.fetchUserProfileData(loadGuardCount: false);
|
||||||
|
} catch (_) {}
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -305,8 +308,11 @@ class _BagsChatboxPageState
|
|||||||
myChatbox = null;
|
myChatbox = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(localizations.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
|
profileManager.syncCurrentUserChatBubble(
|
||||||
|
unload ? null : res.propsResources,
|
||||||
|
expireTime: res.expireTime?.toString(),
|
||||||
|
);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
profileManager.fetchUserProfileData(loadGuardCount: false);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -13,8 +13,6 @@ 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_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/sources/local/floating_screen_manager.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
|
||||||
@ -3864,46 +3862,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String _resolveEntryCompensationCover(PropsResources? resource) {
|
|
||||||
return _firstNonBlankEntryResource([
|
|
||||||
resource?.roomSendCoverUrl,
|
|
||||||
resource?.roomOpenedUrl,
|
|
||||||
resource?.cover,
|
|
||||||
resource?.roomNotOpenedUrl,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _enqueueLowPerformanceEntryCompensation({
|
|
||||||
required Msg joinMsg,
|
|
||||||
required PropsResources resource,
|
|
||||||
required String roomId,
|
|
||||||
}) {
|
|
||||||
if (!SCGlobalConfig.isLowPerformanceDevice ||
|
|
||||||
!shouldShowRoomVisualEffects ||
|
|
||||||
roomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final coverUrl = _resolveEntryCompensationCover(resource);
|
|
||||||
OverlayManager().addLowPerformanceCompensationMessage(
|
|
||||||
SCFloatingMessage(
|
|
||||||
type: 5,
|
|
||||||
priority: 950,
|
|
||||||
userId: joinMsg.user?.id ?? "",
|
|
||||||
roomId: roomId,
|
|
||||||
userAvatarUrl: joinMsg.user?.userAvatar ?? "",
|
|
||||||
userName: joinMsg.user?.userNickname ?? "",
|
|
||||||
giftUrl: coverUrl,
|
|
||||||
fallbackUrl: coverUrl,
|
|
||||||
),
|
|
||||||
dedupKey: [
|
|
||||||
"entry",
|
|
||||||
roomId,
|
|
||||||
joinMsg.user?.id ?? "",
|
|
||||||
resource.id ?? "",
|
|
||||||
].join("|"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _playRoomEntryEffects(
|
void _playRoomEntryEffects(
|
||||||
Msg joinMsg, {
|
Msg joinMsg, {
|
||||||
required int entryRequestSerial,
|
required int entryRequestSerial,
|
||||||
@ -3931,24 +3889,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
type: SCGiftVapSvgaManager.entryEffectType,
|
type: SCGiftVapSvgaManager.entryEffectType,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else if (sourceUrl.isNotEmpty &&
|
|
||||||
SCGlobalConfig.isLowPerformanceDevice &&
|
|
||||||
SCGlobalConfig.isEntryVehicleAnimation &&
|
|
||||||
shouldShowRoomVisualEffects &&
|
|
||||||
_isCurrentRoomEntrySession(entryRequestSerial, roomId)) {
|
|
||||||
_roomEntryEffectTimer?.cancel();
|
|
||||||
_roomEntryEffectTimer = Timer(const Duration(milliseconds: 550), () {
|
|
||||||
_roomEntryEffectTimer = null;
|
|
||||||
if (!shouldShowRoomVisualEffects ||
|
|
||||||
!_isCurrentRoomEntrySession(entryRequestSerial, roomId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_enqueueLowPerformanceEntryCompensation(
|
|
||||||
joinMsg: joinMsg,
|
|
||||||
resource: entryResource,
|
|
||||||
roomId: roomId,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rtmProvider?.msgUserJoinListener != null) {
|
if (rtmProvider?.msgUserJoinListener != null) {
|
||||||
@ -5296,11 +5236,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final targetIndex = micGoUpRes.micIndex ?? index;
|
final targetIndex = micGoUpRes.micIndex ?? index;
|
||||||
|
final selfMicMuted = micGoUpRes.micMute ?? false;
|
||||||
final switched = await _switchRoomRtcToBroadcaster(
|
final switched = await _switchRoomRtcToBroadcaster(
|
||||||
muted: (micGoUpRes.micMute ?? false) || isMic,
|
muted: selfMicMuted,
|
||||||
keepAudioPublishing:
|
keepAudioPublishing: selfMicMuted && _currentUserPublishingRoomMusic,
|
||||||
((micGoUpRes.micMute ?? false) || isMic) &&
|
|
||||||
_currentUserPublishingRoomMusic,
|
|
||||||
);
|
);
|
||||||
if (!switched) {
|
if (!switched) {
|
||||||
await _switchRoomRtcToAudience();
|
await _switchRoomRtcToAudience();
|
||||||
@ -5356,6 +5295,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
roomToken: micGoUpRes.roomToken,
|
roomToken: micGoUpRes.roomToken,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
isMic = selfMicMuted;
|
||||||
_syncSelfMicRuntimeState();
|
_syncSelfMicRuntimeState();
|
||||||
if (roomWheatMap[targetIndex]?.micMute ?? false) {
|
if (roomWheatMap[targetIndex]?.micMute ?? false) {
|
||||||
///房主上麦自动解禁麦位
|
///房主上麦自动解禁麦位
|
||||||
|
|||||||
@ -256,13 +256,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
SCFloatingMessage message, {
|
SCFloatingMessage message, {
|
||||||
String? dedupKey,
|
String? dedupKey,
|
||||||
}) {
|
}) {
|
||||||
if (SCGlobalConfig.isLowPerformanceDevice) {
|
|
||||||
OverlayManager().addLowPerformanceCompensationMessage(
|
|
||||||
message,
|
|
||||||
dedupKey: dedupKey,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OverlayManager().addMessage(message);
|
OverlayManager().addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,23 +276,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
].join("|");
|
].join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
String _entryFloatingDedupKey({
|
|
||||||
required String? roomId,
|
|
||||||
required String? userId,
|
|
||||||
required String? resourceId,
|
|
||||||
}) {
|
|
||||||
return ["entry", roomId ?? "", userId ?? "", resourceId ?? ""].join("|");
|
|
||||||
}
|
|
||||||
|
|
||||||
String _resolveEntryCompensationCover(PropsResources? resource) {
|
|
||||||
return _firstNonBlank([
|
|
||||||
resource?.roomSendCoverUrl,
|
|
||||||
resource?.roomOpenedUrl,
|
|
||||||
resource?.cover,
|
|
||||||
resource?.roomNotOpenedUrl,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _firstNonBlank(Iterable<String?> values) {
|
String _firstNonBlank(Iterable<String?> values) {
|
||||||
for (final value in values) {
|
for (final value in values) {
|
||||||
final text = value?.trim();
|
final text = value?.trim();
|
||||||
@ -6387,29 +6363,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
priority: SCGiftVapSvgaManager.entryEffectPriority,
|
priority: SCGiftVapSvgaManager.entryEffectPriority,
|
||||||
type: SCGiftVapSvgaManager.entryEffectType,
|
type: SCGiftVapSvgaManager.entryEffectType,
|
||||||
);
|
);
|
||||||
} else if (SCGlobalConfig.isLowPerformanceDevice &&
|
|
||||||
SCGlobalConfig.isEntryVehicleAnimation &&
|
|
||||||
shouldShowRoomVisualEffects) {
|
|
||||||
final roomId =
|
|
||||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
|
||||||
final coverUrl = _resolveEntryCompensationCover(entryResource);
|
|
||||||
OverlayManager().addLowPerformanceCompensationMessage(
|
|
||||||
SCFloatingMessage(
|
|
||||||
type: 5,
|
|
||||||
priority: 950,
|
|
||||||
userId: msg.user?.id ?? "",
|
|
||||||
roomId: roomId,
|
|
||||||
userAvatarUrl: msg.user?.userAvatar ?? "",
|
|
||||||
userName: msg.user?.userNickname ?? "",
|
|
||||||
giftUrl: coverUrl,
|
|
||||||
fallbackUrl: coverUrl,
|
|
||||||
),
|
|
||||||
dedupKey: _entryFloatingDedupKey(
|
|
||||||
roomId: roomId,
|
|
||||||
userId: msg.user?.id,
|
|
||||||
resourceId: entryResource.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (msg.type == SCRoomMsgType.gift) {
|
} else if (msg.type == SCRoomMsgType.gift) {
|
||||||
@ -6460,46 +6413,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
'giftId=${gift.id} giftName=${gift.giftName}',
|
'giftId=${gift.id} giftName=${gift.giftName}',
|
||||||
);
|
);
|
||||||
SCGiftVapSvgaManager().play(giftSourceUrl);
|
SCGiftVapSvgaManager().play(giftSourceUrl);
|
||||||
} else if (!scIsCpGift(gift) &&
|
|
||||||
SCGlobalConfig.isLowPerformanceDevice &&
|
|
||||||
SCGlobalConfig.isGiftSpecialEffects &&
|
|
||||||
rtcProvider.shouldShowRoomVisualEffects) {
|
|
||||||
final roomId =
|
|
||||||
(msg.msg?.trim().isNotEmpty ?? false)
|
|
||||||
? msg.msg ?? ""
|
|
||||||
: rtcProvider
|
|
||||||
.currenRoom
|
|
||||||
?.roomProfile
|
|
||||||
?.roomProfile
|
|
||||||
?.id ??
|
|
||||||
"";
|
|
||||||
final quantity = msg.number ?? 0;
|
|
||||||
final coins = quantity * (gift.giftCandy ?? 0);
|
|
||||||
_enqueueGiftFloatingMessage(
|
|
||||||
SCFloatingMessage(
|
|
||||||
type: 1,
|
|
||||||
priority: 900,
|
|
||||||
userId: msg.user?.id ?? "",
|
|
||||||
toUserId: msg.toUser?.id ?? "",
|
|
||||||
userAvatarUrl: msg.user?.userAvatar ?? "",
|
|
||||||
userName: msg.user?.userNickname ?? "",
|
|
||||||
toUserName: msg.toUser?.userNickname ?? "",
|
|
||||||
toUserAvatarUrl: msg.toUser?.userAvatar ?? "",
|
|
||||||
giftUrl: gift.giftPhoto,
|
|
||||||
giftId: gift.id,
|
|
||||||
giftTab: _floatingGiftTabFor(gift),
|
|
||||||
number: quantity,
|
|
||||||
coins: coins,
|
|
||||||
roomId: roomId,
|
|
||||||
),
|
|
||||||
dedupKey: _giftFloatingDedupKey(
|
|
||||||
roomId: roomId,
|
|
||||||
senderId: msg.user?.id,
|
|
||||||
receiverId: msg.toUser?.id,
|
|
||||||
giftId: gift.id,
|
|
||||||
quantity: quantity,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'skip player play because visual effects disabled '
|
'skip player play because visual effects disabled '
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import 'package:yumi/shared/business_logic/models/res/login_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_user_identity_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_user_identity_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/models/enum/sc_props_type.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_level_utils.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';
|
||||||
|
|
||||||
@ -92,6 +93,32 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void syncCurrentUserChatBubble(
|
||||||
|
PropsResources? chatBubble, {
|
||||||
|
String? expireTime,
|
||||||
|
}) {
|
||||||
|
final profile = currentUserProfile;
|
||||||
|
if (profile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final useProps = <UseProps>[
|
||||||
|
...?profile.useProps?.where(
|
||||||
|
(item) => item.propsResources?.type != SCPropsType.CHAT_BUBBLE.name,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if (chatBubble != null) {
|
||||||
|
useProps.add(
|
||||||
|
UseProps(
|
||||||
|
expireTime: expireTime ?? "4102444800000",
|
||||||
|
propsResources: chatBubble,
|
||||||
|
userId: profile.id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
syncCurrentUserProfile(profile.copyWith(useProps: useProps));
|
||||||
|
}
|
||||||
|
|
||||||
Future fetchUserProfileData({
|
Future fetchUserProfileData({
|
||||||
bool loadGuardCount = true,
|
bool loadGuardCount = true,
|
||||||
bool refreshFamilyData = false,
|
bool refreshFamilyData = false,
|
||||||
|
|||||||
@ -26,9 +26,7 @@ import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
|
|||||||
typedef FloatingScreenManager = OverlayManager;
|
typedef FloatingScreenManager = OverlayManager;
|
||||||
|
|
||||||
class OverlayManager {
|
class OverlayManager {
|
||||||
static const int _maxLowPerformanceCompensationQueueLength = 8;
|
|
||||||
static const int _maxDeferredRegionRedPacketCount = 12;
|
static const int _maxDeferredRegionRedPacketCount = 12;
|
||||||
static const Duration _compensationDedupWindow = Duration(seconds: 3);
|
|
||||||
static const Duration _redPacketFloatingDedupWindow = Duration(minutes: 30);
|
static const Duration _redPacketFloatingDedupWindow = Duration(minutes: 30);
|
||||||
static const Duration _rocketFloatingFallbackDedupWindow = Duration(
|
static const Duration _rocketFloatingFallbackDedupWindow = Duration(
|
||||||
seconds: 30,
|
seconds: 30,
|
||||||
@ -43,7 +41,6 @@ class OverlayManager {
|
|||||||
(a, b) => b.priority.compareTo(a.priority),
|
(a, b) => b.priority.compareTo(a.priority),
|
||||||
);
|
);
|
||||||
final List<SCFloatingMessage> _deferredRegionRedPacketMessages = [];
|
final List<SCFloatingMessage> _deferredRegionRedPacketMessages = [];
|
||||||
final Map<String, DateTime> _recentCompensationKeys = {};
|
|
||||||
final Map<String, DateTime> _recentFloatingKeys = {};
|
final Map<String, DateTime> _recentFloatingKeys = {};
|
||||||
final Map<int, OverlayEntry> _stackedOverlayEntries = {};
|
final Map<int, OverlayEntry> _stackedOverlayEntries = {};
|
||||||
final Map<int, SCFloatingMessage> _stackedMessages = {};
|
final Map<int, SCFloatingMessage> _stackedMessages = {};
|
||||||
@ -84,28 +81,6 @@ class OverlayManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLowPerformanceCompensationMessage(
|
|
||||||
SCFloatingMessage message, {
|
|
||||||
String? dedupKey,
|
|
||||||
}) {
|
|
||||||
if (_isDisposed ||
|
|
||||||
_isSuppressed ||
|
|
||||||
!SCGlobalConfig.isLowPerformanceDevice ||
|
|
||||||
SCGlobalConfig.isFloatingBroadcastClosed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final key = dedupKey ?? _compensationKeyFor(message);
|
|
||||||
if (_isRecentCompensationDuplicate(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_rememberCompensationKey(key);
|
|
||||||
_trimLowPerformanceCompensationQueue();
|
|
||||||
_enqueueMessage(
|
|
||||||
message,
|
|
||||||
debugLabel: 'low_performance_compensation_${message.type ?? -1}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _enqueueMessage(
|
void _enqueueMessage(
|
||||||
SCFloatingMessage message, {
|
SCFloatingMessage message, {
|
||||||
required String debugLabel,
|
required String debugLabel,
|
||||||
@ -123,27 +98,6 @@ class OverlayManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isRecentCompensationDuplicate(String key) {
|
|
||||||
_pruneRecentCompensationKeys();
|
|
||||||
final recentTime = _recentCompensationKeys[key];
|
|
||||||
if (recentTime == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return DateTime.now().difference(recentTime) < _compensationDedupWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rememberCompensationKey(String key) {
|
|
||||||
_recentCompensationKeys[key] = DateTime.now();
|
|
||||||
_pruneRecentCompensationKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _pruneRecentCompensationKeys() {
|
|
||||||
final now = DateTime.now();
|
|
||||||
_recentCompensationKeys.removeWhere(
|
|
||||||
(_, time) => now.difference(time) > _compensationDedupWindow,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isRecentFloatingDuplicate(SCFloatingMessage message) {
|
bool _isRecentFloatingDuplicate(SCFloatingMessage message) {
|
||||||
_pruneRecentFloatingKeys();
|
_pruneRecentFloatingKeys();
|
||||||
final key = _floatingDedupKeyFor(message);
|
final key = _floatingDedupKeyFor(message);
|
||||||
@ -261,33 +215,6 @@ class OverlayManager {
|
|||||||
return _redPacketFloatingDedupWindow;
|
return _redPacketFloatingDedupWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _compensationKeyFor(SCFloatingMessage message) {
|
|
||||||
return [
|
|
||||||
message.type,
|
|
||||||
message.roomId,
|
|
||||||
message.userId,
|
|
||||||
message.toUserId,
|
|
||||||
message.giftId,
|
|
||||||
message.number,
|
|
||||||
message.giftUrl,
|
|
||||||
].join('|');
|
|
||||||
}
|
|
||||||
|
|
||||||
void _trimLowPerformanceCompensationQueue() {
|
|
||||||
if (_messageQueue.length < _maxLowPerformanceCompensationQueueLength) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final retained =
|
|
||||||
_messageQueue.unorderedElements.cast<SCFloatingMessage>().toList()
|
|
||||||
..sort((a, b) => b.priority.compareTo(a.priority));
|
|
||||||
_messageQueue.clear();
|
|
||||||
for (final message in retained.take(
|
|
||||||
_maxLowPerformanceCompensationQueueLength - 1,
|
|
||||||
)) {
|
|
||||||
_messageQueue.add(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _warmMessageImages(SCFloatingMessage message) async {
|
Future<void> _warmMessageImages(SCFloatingMessage message) async {
|
||||||
unawaited(
|
unawaited(
|
||||||
warmImageResource(
|
warmImageResource(
|
||||||
@ -671,7 +598,7 @@ class OverlayManager {
|
|||||||
SCGlobalConfig.floatingBroadcastScope = scope;
|
SCGlobalConfig.floatingBroadcastScope = scope;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final defaultEffectsEnabled = !SCGlobalConfig.isLowPerformanceDevice;
|
const defaultEffectsEnabled = true;
|
||||||
final isLegacyFloatingAnimationInGlobal = DataPersistence.getBool(
|
final isLegacyFloatingAnimationInGlobal = DataPersistence.getBool(
|
||||||
SCGlobalConfig.legacyFloatingAnimationInGlobalStorageKey(account),
|
SCGlobalConfig.legacyFloatingAnimationInGlobalStorageKey(account),
|
||||||
defaultValue: defaultEffectsEnabled,
|
defaultValue: defaultEffectsEnabled,
|
||||||
@ -752,7 +679,6 @@ class OverlayManager {
|
|||||||
_removeAllStackedMessages();
|
_removeAllStackedMessages();
|
||||||
_messageQueue.clear();
|
_messageQueue.clear();
|
||||||
_deferredRegionRedPacketMessages.clear();
|
_deferredRegionRedPacketMessages.clear();
|
||||||
_recentCompensationKeys.clear();
|
|
||||||
_isPlaying = false;
|
_isPlaying = false;
|
||||||
_isProcessing = false;
|
_isProcessing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,20 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
|
|
||||||
class SCLuckyGiftWinSoundPlayer {
|
class SCLuckyGiftWinSoundPlayer {
|
||||||
static const String assetPath =
|
static const String assetPath =
|
||||||
"sc_images/room/anim/luck_gift/lucky_gift_win.mp3";
|
"sc_images/room/anim/luck_gift/lucky_gift_win.mp3";
|
||||||
static const Duration _sameEventDedupeWindow = Duration(seconds: 3);
|
static const Duration _stopDelay = Duration(milliseconds: 3200);
|
||||||
|
static const double _volume = 0.75;
|
||||||
|
|
||||||
static final AudioPlayer _player = AudioPlayer();
|
static final AudioPlayer _player = AudioPlayer();
|
||||||
static String? _lastEventKey;
|
static bool _loaded = false;
|
||||||
static DateTime? _lastPlayedAt;
|
static bool _playing = false;
|
||||||
|
static Future<void>? _loadFuture;
|
||||||
|
static Future<void>? _startFuture;
|
||||||
|
static Timer? _stopTimer;
|
||||||
|
|
||||||
static String buildEventKey({
|
static String buildEventKey({
|
||||||
String? giftId,
|
String? giftId,
|
||||||
@ -26,25 +32,75 @@ class SCLuckyGiftWinSoundPlayer {
|
|||||||
|
|
||||||
static Future<void> play({String? eventKey}) async {
|
static Future<void> play({String? eventKey}) async {
|
||||||
if (DataPersistence.getPlayGiftMusic()) {
|
if (DataPersistence.getPlayGiftMusic()) {
|
||||||
|
unawaited(stop());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final normalizedEventKey = (eventKey ?? '').trim();
|
|
||||||
final now = DateTime.now();
|
|
||||||
final lastPlayedAt = _lastPlayedAt;
|
|
||||||
if (normalizedEventKey.isNotEmpty &&
|
|
||||||
normalizedEventKey == _lastEventKey &&
|
|
||||||
lastPlayedAt != null &&
|
|
||||||
now.difference(lastPlayedAt) < _sameEventDedupeWindow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastEventKey = normalizedEventKey.isEmpty ? null : normalizedEventKey;
|
|
||||||
_lastPlayedAt = now;
|
|
||||||
try {
|
try {
|
||||||
await _player.stop();
|
await _ensureLoaded();
|
||||||
await _player.play(AssetSource(assetPath));
|
_extendStopWindow();
|
||||||
} catch (error) {}
|
|
||||||
|
if (_playing || _startFuture != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startFuture = _startPlayback();
|
||||||
|
await _startFuture;
|
||||||
|
} catch (error) {
|
||||||
|
_startFuture = null;
|
||||||
|
}
|
||||||
|
_startFuture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> stop() async {
|
||||||
|
_stopTimer?.cancel();
|
||||||
|
_stopTimer = null;
|
||||||
|
try {
|
||||||
|
if (_player.state != PlayerState.stopped) {
|
||||||
|
await _player.stop();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
_playing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _ensureLoaded() {
|
||||||
|
if (_loaded) {
|
||||||
|
return Future<void>.value();
|
||||||
|
}
|
||||||
|
final loadFuture = _loadFuture;
|
||||||
|
if (loadFuture != null) {
|
||||||
|
return loadFuture;
|
||||||
|
}
|
||||||
|
_loadFuture = _load();
|
||||||
|
return _loadFuture!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _load() async {
|
||||||
|
try {
|
||||||
|
await _player.setReleaseMode(ReleaseMode.loop);
|
||||||
|
await _player.setVolume(_volume);
|
||||||
|
await _player.setSource(AssetSource(assetPath));
|
||||||
|
_loaded = true;
|
||||||
|
} catch (error) {
|
||||||
|
_loadFuture = null;
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _startPlayback() async {
|
||||||
|
await _player.seek(Duration.zero);
|
||||||
|
await _player.resume();
|
||||||
|
_playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _extendStopWindow() {
|
||||||
|
_stopTimer?.cancel();
|
||||||
|
_stopTimer = Timer(_stopDelay, () {
|
||||||
|
unawaited(stop());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _formatNum(num? value) {
|
static String _formatNum(num? value) {
|
||||||
|
|||||||
@ -288,7 +288,7 @@ class SCChatRoomHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void roomSCGlobalConfig(String roomId) {
|
static void roomSCGlobalConfig(String roomId) {
|
||||||
final defaultEffectsEnabled = !SCGlobalConfig.isLowPerformanceDevice;
|
const defaultEffectsEnabled = true;
|
||||||
final account = AccountStorage().getCurrentUser()?.userProfile?.account;
|
final account = AccountStorage().getCurrentUser()?.userProfile?.account;
|
||||||
SCGlobalConfig
|
SCGlobalConfig
|
||||||
.isGiftSpecialEffects = SCGlobalConfig.clampVisualEffectPreference(
|
.isGiftSpecialEffects = SCGlobalConfig.clampVisualEffectPreference(
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'package:extended_image/extended_image.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_debouncer/flutter_debouncer.dart';
|
import 'package:flutter_debouncer/flutter_debouncer.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
@ -70,7 +68,11 @@ class _PropsBagChatboxDetailDialogState
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 25.w),
|
SizedBox(height: 25.w),
|
||||||
netImage(url: widget.res.propsResources?.cover??"",width: 80.w,height: 25.w),
|
netImage(
|
||||||
|
url: widget.res.propsResources?.cover ?? "",
|
||||||
|
width: 80.w,
|
||||||
|
height: 25.w,
|
||||||
|
),
|
||||||
SizedBox(height: 30.w),
|
SizedBox(height: 30.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -136,6 +138,11 @@ class _PropsBagChatboxDetailDialogState
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _use(BagsListRes res, bool unload) {
|
void _use(BagsListRes res, bool unload) {
|
||||||
|
final localizations = SCAppLocalizations.of(context)!;
|
||||||
|
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
SCLoadingManager.show();
|
SCLoadingManager.show();
|
||||||
SCStoreRepositoryImp()
|
SCStoreRepositoryImp()
|
||||||
.switchPropsUse(
|
.switchPropsUse(
|
||||||
@ -144,18 +151,25 @@ class _PropsBagChatboxDetailDialogState
|
|||||||
unload,
|
unload,
|
||||||
)
|
)
|
||||||
.then((value) async {
|
.then((value) async {
|
||||||
await Provider.of<SocialChatUserProfileManager>(
|
try {
|
||||||
context,
|
await profileManager.fetchUserProfileData(loadGuardCount: false);
|
||||||
listen: false,
|
} catch (_) {}
|
||||||
).fetchUserProfileData(loadGuardCount: false);
|
if (!mounted) {
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
profileManager.syncCurrentUserChatBubble(
|
||||||
|
unload ? null : res.propsResources,
|
||||||
|
expireTime: res.expireTime?.toString(),
|
||||||
|
);
|
||||||
Future.delayed(Duration(milliseconds: 400), () {
|
Future.delayed(Duration(milliseconds: 400), () {
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
SmartDialog.dismiss(tag: "showPropsDetail");
|
SmartDialog.dismiss(tag: "showPropsDetail");
|
||||||
});
|
});
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.successfulWear);
|
SCTts.show(localizations.successfulWear);
|
||||||
} else {
|
} else {
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.successfullyUnloaded);
|
SCTts.show(localizations.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
|
|||||||
@ -65,10 +65,6 @@ class _RoomAnimationQueueScreenState extends State<RoomAnimationQueueScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _enqueueVipEntryFloatingMessage(Msg msg) {
|
void _enqueueVipEntryFloatingMessage(Msg msg) {
|
||||||
if (SCGlobalConfig.isLowPerformanceDevice &&
|
|
||||||
((msg.user?.getMountains()?.sourceUrl ?? "").trim().isNotEmpty)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final floatPicture = msg.user?.getFloatPicture();
|
final floatPicture = msg.user?.getFloatPicture();
|
||||||
final floatPictureUrl = _resolveVipFloatPictureUrl(floatPicture);
|
final floatPictureUrl = _resolveVipFloatPictureUrl(floatPicture);
|
||||||
final floatPictureFallbackUrl = _resolveVipFloatPictureFallbackUrl(
|
final floatPictureFallbackUrl = _resolveVipFloatPictureFallbackUrl(
|
||||||
@ -86,33 +82,16 @@ class _RoomAnimationQueueScreenState extends State<RoomAnimationQueueScreen> {
|
|||||||
if (roomId.isEmpty) {
|
if (roomId.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final isLowPerformance = SCGlobalConfig.isLowPerformanceDevice;
|
|
||||||
final displayUrl =
|
|
||||||
isLowPerformance && floatPictureFallbackUrl.isNotEmpty
|
|
||||||
? floatPictureFallbackUrl
|
|
||||||
: floatPictureUrl;
|
|
||||||
final floatingMessage = SCFloatingMessage(
|
final floatingMessage = SCFloatingMessage(
|
||||||
type: 5,
|
type: 5,
|
||||||
userId: msg.user?.id,
|
userId: msg.user?.id,
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
userAvatarUrl: msg.user?.userAvatar,
|
userAvatarUrl: msg.user?.userAvatar,
|
||||||
userName: msg.user?.userNickname,
|
userName: msg.user?.userNickname,
|
||||||
giftUrl: displayUrl,
|
giftUrl: floatPictureUrl,
|
||||||
fallbackUrl: floatPictureFallbackUrl,
|
fallbackUrl: floatPictureFallbackUrl,
|
||||||
priority: 900,
|
priority: 900,
|
||||||
);
|
);
|
||||||
if (isLowPerformance) {
|
|
||||||
OverlayManager().addLowPerformanceCompensationMessage(
|
|
||||||
floatingMessage,
|
|
||||||
dedupKey: [
|
|
||||||
"entry_float",
|
|
||||||
roomId,
|
|
||||||
msg.user?.id ?? "",
|
|
||||||
floatPicture?.id ?? "",
|
|
||||||
].join("|"),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
OverlayManager().addMessage(floatingMessage);
|
OverlayManager().addMessage(floatingMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user