Compare commits
No commits in common. "84b68fbb4ae04907a8a152f3874cde03ec05a1f1" and "74e8162c1fd49d9a82fe73c8433e8489bfc30f57" have entirely different histories.
84b68fbb4a
...
74e8162c1f
@ -676,7 +676,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
color: const Color(0xff09372E).withValues(alpha: 0.5),
|
color: const Color(0xff09372E).withValues(alpha: 0.5),
|
||||||
constraints: BoxConstraints(maxHeight: 430.w),
|
constraints: BoxConstraints(maxHeight: 430.w),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 12.w),
|
SizedBox(height: 12.w),
|
||||||
Row(
|
Row(
|
||||||
@ -783,8 +782,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
SizedBox(width: 5.w),
|
SizedBox(width: 5.w),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(
|
Expanded(
|
||||||
height: kGiftTabPageHeight.w,
|
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
physics: NeverScrollableScrollPhysics(),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
|
|||||||
@ -18,29 +18,6 @@ enum _GiftGridPageStatus { idle, loading, ready }
|
|||||||
|
|
||||||
const Duration _kGiftPageSkeletonMinDuration = Duration(milliseconds: 420);
|
const Duration _kGiftPageSkeletonMinDuration = Duration(milliseconds: 420);
|
||||||
const Duration _kGiftPageSkeletonMaxDuration = Duration(milliseconds: 900);
|
const Duration _kGiftPageSkeletonMaxDuration = Duration(milliseconds: 900);
|
||||||
const int _kGiftGridColumnCount = 4;
|
|
||||||
const int _kGiftGridRowCount = 2;
|
|
||||||
const double _kGiftGridCardHeight = 76;
|
|
||||||
const double _kGiftGridVerticalPadding = 5;
|
|
||||||
const double _kGiftGridMainAxisSpacing = 2;
|
|
||||||
const double _kGiftGridCrossAxisSpacing = 8;
|
|
||||||
const double _kGiftCardVerticalPadding = 5;
|
|
||||||
const double _kGiftIconPriceSpacing = 1;
|
|
||||||
const double _kGiftPriceRowHeight = 14;
|
|
||||||
const double _kGiftGridTopGap = 5;
|
|
||||||
const double _kGiftIndicatorTopGap = 5;
|
|
||||||
const double _kGiftPageIndicatorHeight = 6.5;
|
|
||||||
const double _kGiftGridBottomPadding = 6;
|
|
||||||
const double _kGiftGridHeight =
|
|
||||||
_kGiftGridCardHeight * _kGiftGridRowCount +
|
|
||||||
_kGiftGridMainAxisSpacing * (_kGiftGridRowCount - 1) +
|
|
||||||
_kGiftGridVerticalPadding * 2;
|
|
||||||
const double kGiftTabPageHeight =
|
|
||||||
_kGiftGridTopGap +
|
|
||||||
_kGiftGridHeight +
|
|
||||||
_kGiftIndicatorTopGap +
|
|
||||||
_kGiftPageIndicatorHeight +
|
|
||||||
_kGiftGridBottomPadding;
|
|
||||||
|
|
||||||
class GiftTabPage extends StatefulWidget {
|
class GiftTabPage extends StatefulWidget {
|
||||||
final String type;
|
final String type;
|
||||||
@ -95,9 +72,8 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
))
|
))
|
||||||
: Column(
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: _kGiftGridTopGap.w),
|
SizedBox(height: 23.w),
|
||||||
SizedBox(
|
Expanded(
|
||||||
height: _kGiftGridHeight.w,
|
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
controller: _giftPageController,
|
controller: _giftPageController,
|
||||||
onPageChanged: (i) {
|
onPageChanged: (i) {
|
||||||
@ -119,7 +95,16 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
_GiftGridPageStatus.ready) {
|
_GiftGridPageStatus.ready) {
|
||||||
return _buildGiftPageSkeleton();
|
return _buildGiftPageSkeleton();
|
||||||
}
|
}
|
||||||
return _buildGiftGrid(
|
return GridView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 4,
|
||||||
|
childAspectRatio: 0.75,
|
||||||
|
mainAxisSpacing: 8.w,
|
||||||
|
crossAxisSpacing: 8.w,
|
||||||
|
),
|
||||||
itemCount: size,
|
itemCount: size,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return _bagItem(gifts[(i * 8) + index], ref);
|
return _bagItem(gifts[(i * 8) + index], ref);
|
||||||
@ -129,9 +114,8 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
itemCount: (gifts.length / 8).ceil(),
|
itemCount: (gifts.length / 8).ceil(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: _kGiftIndicatorTopGap.w),
|
|
||||||
_indicator(ref),
|
_indicator(ref),
|
||||||
SizedBox(height: _kGiftGridBottomPadding.w),
|
SizedBox(height: 10.w),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -222,12 +206,19 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
GridView.builder(
|
||||||
child: _buildGiftGrid(
|
shrinkWrap: true,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 4,
|
||||||
|
childAspectRatio: 0.75,
|
||||||
|
mainAxisSpacing: 8.w,
|
||||||
|
crossAxisSpacing: 8.w,
|
||||||
|
),
|
||||||
itemCount: 8,
|
itemCount: 8,
|
||||||
itemBuilder: (context, index) => _buildGiftSkeletonCard(),
|
itemBuilder: (context, index) => _buildGiftSkeletonCard(),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -235,11 +226,12 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
Widget _buildGiftSkeletonCard() {
|
Widget _buildGiftSkeletonCard() {
|
||||||
final baseColor = Colors.white.withValues(alpha: 0.12);
|
final baseColor = Colors.white.withValues(alpha: 0.12);
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12.w)),
|
decoration: BoxDecoration(
|
||||||
child: Padding(
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
padding: EdgeInsets.symmetric(vertical: _kGiftCardVerticalPadding.w),
|
color: Colors.white.withValues(alpha: 0.08),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 48.w,
|
width: 48.w,
|
||||||
@ -250,7 +242,7 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
),
|
),
|
||||||
child: Center(child: SCRotatingDotsLoading(size: 24.w)),
|
child: Center(child: SCRotatingDotsLoading(size: 24.w)),
|
||||||
),
|
),
|
||||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
SizedBox(height: 7.w),
|
||||||
Container(
|
Container(
|
||||||
width: 52.w,
|
width: 52.w,
|
||||||
height: 8.w,
|
height: 8.w,
|
||||||
@ -259,47 +251,17 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
color: baseColor,
|
color: baseColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 6.w),
|
||||||
|
Container(
|
||||||
|
width: 40.w,
|
||||||
|
height: 8.w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(999.w),
|
||||||
|
color: baseColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildGiftGrid({
|
|
||||||
required int itemCount,
|
|
||||||
required IndexedWidgetBuilder itemBuilder,
|
|
||||||
}) {
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final horizontalPadding = 12.w;
|
|
||||||
final verticalPadding = _kGiftGridVerticalPadding.w;
|
|
||||||
final mainAxisSpacing = _kGiftGridMainAxisSpacing.w;
|
|
||||||
final crossAxisSpacing = _kGiftGridCrossAxisSpacing.w;
|
|
||||||
final itemHeight = _kGiftGridCardHeight.w;
|
|
||||||
final gridWidth =
|
|
||||||
constraints.maxWidth -
|
|
||||||
horizontalPadding * 2 -
|
|
||||||
crossAxisSpacing * (_kGiftGridColumnCount - 1);
|
|
||||||
final itemWidth = (gridWidth / _kGiftGridColumnCount).clamp(
|
|
||||||
1.0,
|
|
||||||
double.infinity,
|
|
||||||
);
|
|
||||||
return GridView.builder(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: horizontalPadding,
|
|
||||||
vertical: verticalPadding,
|
|
||||||
),
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: _kGiftGridColumnCount,
|
|
||||||
childAspectRatio: itemWidth / itemHeight,
|
|
||||||
mainAxisSpacing: mainAxisSpacing,
|
|
||||||
crossAxisSpacing: crossAxisSpacing,
|
|
||||||
),
|
|
||||||
itemCount: itemCount,
|
|
||||||
itemBuilder: itemBuilder,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,20 +297,16 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
Widget _bagItem(SocialChatGiftRes gift, SCAppGeneralManager ref) {
|
Widget _bagItem(SocialChatGiftRes gift, SCAppGeneralManager ref) {
|
||||||
return gift.id == "-1000"
|
return gift.id == "-1000"
|
||||||
? GestureDetector(
|
? GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
|
color: Colors.white10,
|
||||||
border: Border.all(color: Colors.transparent, width: 1.w),
|
border: Border.all(color: Colors.transparent, width: 1.w),
|
||||||
),
|
),
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: _kGiftCardVerticalPadding.w,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
Image.asset(
|
||||||
_strategy.getGiftPageCustomizedRuleIcon(),
|
_strategy.getGiftPageCustomizedRuleIcon(),
|
||||||
@ -356,15 +314,15 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
width: 48.w,
|
width: 48.w,
|
||||||
height: 48.w,
|
height: 48.w,
|
||||||
),
|
),
|
||||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
SizedBox(height: 5.w),
|
||||||
Container(
|
Container(
|
||||||
height: 12.w,
|
height: 23.w,
|
||||||
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: text(
|
child: text(
|
||||||
SCAppLocalizations.of(context)!.rulesUpload,
|
SCAppLocalizations.of(context)!.rulesUpload,
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
fontSize: 10,
|
fontSize: 10.sp,
|
||||||
letterSpacing: 0.1,
|
letterSpacing: 0.1,
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -375,7 +333,6 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -446,10 +403,10 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
: GestureDetector(
|
: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
|
color: Colors.white10,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color:
|
color:
|
||||||
checkedIndex == ref.giftByTab[widget.type]!.indexOf(gift)
|
checkedIndex == ref.giftByTab[widget.type]!.indexOf(gift)
|
||||||
@ -458,12 +415,8 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
width: 1.w,
|
width: 1.w,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: _kGiftCardVerticalPadding.w,
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
netImage(
|
netImage(
|
||||||
url: gift.giftPhoto ?? "",
|
url: gift.giftPhoto ?? "",
|
||||||
@ -473,18 +426,30 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
loadingWidget: _buildGiftCoverLoading(),
|
loadingWidget: _buildGiftCoverLoading(),
|
||||||
errorWidget: _buildGiftCoverNoData(),
|
errorWidget: _buildGiftCoverNoData(),
|
||||||
),
|
),
|
||||||
SizedBox(height: _kGiftIconPriceSpacing.w),
|
SizedBox(height: 5.w),
|
||||||
SizedBox(
|
Container(
|
||||||
height: _kGiftPriceRowHeight.w,
|
height: 23.w,
|
||||||
child: Row(
|
margin: EdgeInsets.symmetric(horizontal: 3.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: text(
|
||||||
|
gift.giftName ?? "",
|
||||||
|
maxLines: 2,
|
||||||
|
fontSize: 10.sp,
|
||||||
|
letterSpacing: 0.1,
|
||||||
|
lineHeight: 1,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
textColor: widget.isDark ? Colors.white : Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children:
|
children:
|
||||||
_isBackpackTab
|
_isBackpackTab
|
||||||
? [
|
? [
|
||||||
text(
|
text(
|
||||||
"x${_formatBackpackQuantity(gift)}",
|
"x${_formatBackpackQuantity(gift)}",
|
||||||
fontSize: 10,
|
fontSize: 10.sp,
|
||||||
lineHeight: 1,
|
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
textColor:
|
textColor:
|
||||||
widget.isDark
|
widget.isDark
|
||||||
@ -501,20 +466,15 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
SizedBox(width: 3.w),
|
SizedBox(width: 3.w),
|
||||||
text(
|
text(
|
||||||
"${gift.giftCandy}",
|
"${gift.giftCandy}",
|
||||||
fontSize: 10,
|
fontSize: 10.sp,
|
||||||
lineHeight: 1,
|
|
||||||
textColor:
|
textColor:
|
||||||
widget.isDark
|
widget.isDark ? Colors.white : Colors.black,
|
||||||
? Colors.white
|
|
||||||
: Colors.black,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
checkedIndex = ref.giftByTab[widget.type]!.indexOf(gift);
|
checkedIndex = ref.giftByTab[widget.type]!.indexOf(gift);
|
||||||
|
|||||||
@ -1,168 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_backpack_res.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
|
||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
|
||||||
|
|
||||||
const double _kBagsGiftGridAspectRatio = 0.86;
|
|
||||||
|
|
||||||
///背包-礼物
|
|
||||||
class BagsGiftPage extends SCPageList {
|
|
||||||
const BagsGiftPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
SCPageListState<SocialChatGiftBackpackRes, BagsGiftPage> createState() =>
|
|
||||||
_BagsGiftPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BagsGiftPageState
|
|
||||||
extends SCPageListState<SocialChatGiftBackpackRes, BagsGiftPage> {
|
|
||||||
SocialChatGiftBackpackRes? selectedGift;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
enablePullUp = false;
|
|
||||||
isGridView = true;
|
|
||||||
gridViewCount = 3;
|
|
||||||
padding = EdgeInsets.symmetric(horizontal: 6.w);
|
|
||||||
backgroundColor = Colors.transparent;
|
|
||||||
gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: gridViewCount,
|
|
||||||
mainAxisSpacing: 12.w,
|
|
||||||
crossAxisSpacing: 12.w,
|
|
||||||
childAspectRatio: _kBagsGiftGridAspectRatio,
|
|
||||||
);
|
|
||||||
loadData(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
body:
|
|
||||||
items.isEmpty && isLoading
|
|
||||||
? const SCBagGridSkeleton()
|
|
||||||
: buildList(context),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildItem(SocialChatGiftBackpackRes res) {
|
|
||||||
final gift = res.giftConfig;
|
|
||||||
final isSelected = selectedGift?.id == res.id;
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
selectedGift = res;
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xff18F2B1).withValues(alpha: 0.1),
|
|
||||||
border: Border.all(
|
|
||||||
color:
|
|
||||||
isSelected ? SocialChatTheme.primaryLight : Colors.transparent,
|
|
||||||
width: 1.w,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(height: 25.w),
|
|
||||||
netImage(
|
|
||||||
url: gift?.giftPhoto ?? "",
|
|
||||||
width: 55.w,
|
|
||||||
height: 55.w,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
buildStoreBagItemTitle(
|
|
||||||
gift?.giftName ?? "",
|
|
||||||
textColor: Colors.white,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
SizedBox(height: 6.w),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
SCGlobalConfig.businessLogicStrategy
|
|
||||||
.getGiftPageGoldCoinIcon(),
|
|
||||||
width: 14.w,
|
|
||||||
height: 14.w,
|
|
||||||
),
|
|
||||||
SizedBox(width: 3.w),
|
|
||||||
text(
|
|
||||||
_formatNumber(gift?.giftCandy),
|
|
||||||
fontSize: 10,
|
|
||||||
lineHeight: 1,
|
|
||||||
textColor: Colors.white,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
PositionedDirectional(
|
|
||||||
top: 0,
|
|
||||||
start: 0,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.w),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: SocialChatTheme.primaryLight,
|
|
||||||
borderRadius: BorderRadiusDirectional.only(
|
|
||||||
topStart: Radius.circular(6.w),
|
|
||||||
topEnd: Radius.circular(12.w),
|
|
||||||
bottomEnd: Radius.circular(12.w),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: text(
|
|
||||||
"x${_formatNumber(res.quantity)}",
|
|
||||||
fontSize: 10,
|
|
||||||
lineHeight: 1,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
textColor: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
loadPage({
|
|
||||||
required int page,
|
|
||||||
required Function(List<SocialChatGiftBackpackRes>) onSuccess,
|
|
||||||
Function? onErr,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
final giftList = await SCChatRoomRepository().giftBackpack();
|
|
||||||
onSuccess(giftList.where((item) => (item.quantity ?? 0) > 0).toList());
|
|
||||||
} catch (e) {
|
|
||||||
if (onErr != null) {
|
|
||||||
onErr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatNumber(num? value) {
|
|
||||||
if (value == null) {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
if (value % 1 == 0) {
|
|
||||||
return value.toInt().toString();
|
|
||||||
}
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,23 +1,31 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:yumi/modules/user/my_items/theme/bags_tab_theme_page.dart';
|
||||||
|
import 'package:tancent_vap/utils/constant.dart';
|
||||||
|
import 'package:tancent_vap/widgets/vap_view.dart';
|
||||||
|
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart';
|
||||||
import 'package:yumi/modules/store/store_route.dart';
|
import 'package:yumi/modules/store/store_route.dart';
|
||||||
import 'package:yumi/modules/user/my_items/chatbox/bags_chatbox_page.dart';
|
import 'package:yumi/modules/user/my_items/chatbox/bags_chatbox_page.dart';
|
||||||
import 'package:yumi/modules/user/my_items/gift/bags_gift_page.dart';
|
|
||||||
import 'package:yumi/modules/user/my_items/headdress/bags_headdress_page.dart';
|
import 'package:yumi/modules/user/my_items/headdress/bags_headdress_page.dart';
|
||||||
import 'package:yumi/modules/user/my_items/mountains/bags_mountains_page.dart';
|
import 'package:yumi/modules/user/my_items/mountains/bags_mountains_page.dart';
|
||||||
|
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
import '../../../app/constants/sc_global_config.dart';
|
||||||
import 'package:yumi/shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
import '../../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
||||||
|
|
||||||
class MyItemsPage extends StatefulWidget {
|
class MyItemsPage extends StatefulWidget {
|
||||||
const MyItemsPage({super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MyItemsPage> createState() => _MyItemsPageState();
|
_MyItemsPageState createState() => _MyItemsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyItemsPageState extends State<MyItemsPage>
|
class _MyItemsPageState extends State<MyItemsPage>
|
||||||
@ -30,16 +38,20 @@ class _MyItemsPageState extends State<MyItemsPage>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_pages.add(BagsHeaddressPage());
|
_pages.add(BagsHeaddressPage());
|
||||||
_pages.add(BagsMountainsPage());
|
_pages.add(
|
||||||
_pages.add(BagsGiftPage());
|
BagsMountainsPage(),
|
||||||
|
);
|
||||||
|
_pages.add(
|
||||||
|
BagsTabThemePage(),
|
||||||
|
);
|
||||||
_pages.add(BagsChatboxPage());
|
_pages.add(BagsChatboxPage());
|
||||||
_tabController = TabController(length: _pages.length, vsync: this);
|
_tabController = TabController(length: _pages.length, vsync: this);
|
||||||
_tabController.addListener(() {});
|
_tabController.addListener(() {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_tabController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +60,7 @@ class _MyItemsPageState extends State<MyItemsPage>
|
|||||||
_tabs.clear();
|
_tabs.clear();
|
||||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.headdress));
|
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.headdress));
|
||||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains));
|
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains));
|
||||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.gift));
|
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.theme));
|
||||||
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox));
|
_tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox));
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -76,18 +88,12 @@ class _MyItemsPageState extends State<MyItemsPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SCNavigatorUtils.push(
|
SCNavigatorUtils.push(context, StoreRoute.list, replace: true);
|
||||||
context,
|
|
||||||
StoreRoute.list,
|
|
||||||
replace: true,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(top:false,child: Column(
|
||||||
top: false,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -125,14 +131,10 @@ class _MyItemsPageState extends State<MyItemsPage>
|
|||||||
),
|
),
|
||||||
SizedBox(height: 5.w),
|
SizedBox(height: 5.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(controller: _tabController, children: _pages),
|
||||||
controller: _tabController,
|
|
||||||
children: _pages,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -56,7 +56,6 @@ import '../../shared/business_logic/models/res/sc_room_theme_list_res.dart';
|
|||||||
import '../../shared/business_logic/models/res/sc_vip_res.dart';
|
import '../../shared/business_logic/models/res/sc_vip_res.dart';
|
||||||
import '../../shared/tools/sc_room_profile_cache.dart';
|
import '../../shared/tools/sc_room_profile_cache.dart';
|
||||||
import '../../ui_kit/components/sc_float_ichart.dart';
|
import '../../ui_kit/components/sc_float_ichart.dart';
|
||||||
import '../../ui_kit/widgets/room/rocket/room_rocket_asset_preloader.dart';
|
|
||||||
import '../../ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart';
|
import '../../ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart';
|
||||||
import '../../ui_kit/widgets/room/rocket/room_rocket_api_mapper.dart';
|
import '../../ui_kit/widgets/room/rocket/room_rocket_api_mapper.dart';
|
||||||
import '../../ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
import '../../ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
||||||
@ -2766,7 +2765,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
.then((res) {
|
.then((res) {
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_scheduleRoomRocketAssetPreload(roomId, res, afterRoomEntry: true);
|
|
||||||
})
|
})
|
||||||
.catchError((e) {});
|
.catchError((e) {});
|
||||||
_loadRoomRocketRewardPopups(roomId);
|
_loadRoomRocketRewardPopups(roomId);
|
||||||
@ -2827,10 +2825,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
||||||
_scheduleRoomRocketAssetPreload(
|
|
||||||
(res.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(),
|
|
||||||
res,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///刷新房间火箭信息
|
///刷新房间火箭信息
|
||||||
@ -2849,35 +2843,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
||||||
_scheduleRoomRocketAssetPreload(resolvedRoomId, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scheduleRoomRocketAssetPreload(
|
|
||||||
String roomId,
|
|
||||||
SCRoomRocketStatusRes status, {
|
|
||||||
bool afterRoomEntry = false,
|
|
||||||
}) {
|
|
||||||
final resolvedRoomId = roomId.trim();
|
|
||||||
if (resolvedRoomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (afterRoomEntry) {
|
|
||||||
RoomRocketAssetPreloader.preloadAfterRoomEntry(
|
|
||||||
roomId: resolvedRoomId,
|
|
||||||
status: status,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
RoomRocketAssetPreloader.preloadForStatus(
|
|
||||||
roomId: resolvedRoomId,
|
|
||||||
status: status,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (RoomRocketAssetPreloader.shouldForceCurrentLaunchPreload(status)) {
|
|
||||||
RoomRocketAssetPreloader.forcePreloadCurrentLaunchAssets(
|
|
||||||
roomId: resolvedRoomId,
|
|
||||||
status: status,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleRoomRocketStatusRefreshForGift({String? roomId}) {
|
void scheduleRoomRocketStatusRefreshForGift({String? roomId}) {
|
||||||
@ -3681,7 +3646,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomRocketGiftRefreshTimer = null;
|
_roomRocketGiftRefreshTimer = null;
|
||||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||||
_lastRoomRocketLaunchNoticeKey = null;
|
_lastRoomRocketLaunchNoticeKey = null;
|
||||||
RoomRocketAssetPreloader.cancelCurrentRoom();
|
|
||||||
_stopRoomRedPacketPresenceHeartbeat();
|
_stopRoomRedPacketPresenceHeartbeat();
|
||||||
_previewRoomSeatCount = null;
|
_previewRoomSeatCount = null;
|
||||||
_finishRoomStartupSeatLoading(executePendingAction: false);
|
_finishRoomStartupSeatLoading(executePendingAction: false);
|
||||||
|
|||||||
@ -231,8 +231,6 @@ abstract class SocialChatRoomRepository {
|
|||||||
String roomId, {
|
String roomId, {
|
||||||
int cursor = 1,
|
int cursor = 1,
|
||||||
int limit = 10,
|
int limit = 10,
|
||||||
String? launchNo,
|
|
||||||
bool roomScope = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
///查询火箭规则说明
|
///查询火箭规则说明
|
||||||
|
|||||||
@ -1133,16 +1133,11 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
String roomId, {
|
String roomId, {
|
||||||
int cursor = 1,
|
int cursor = 1,
|
||||||
int limit = 10,
|
int limit = 10,
|
||||||
String? launchNo,
|
|
||||||
bool roomScope = false,
|
|
||||||
}) async {
|
}) async {
|
||||||
final resolvedLaunchNo = launchNo?.trim() ?? '';
|
|
||||||
final queryParams = {
|
final queryParams = {
|
||||||
if (roomId.trim().isNotEmpty) "roomId": roomId,
|
if (roomId.trim().isNotEmpty) "roomId": roomId,
|
||||||
"cursor": cursor,
|
"cursor": cursor,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
if (resolvedLaunchNo.isNotEmpty) "launchNo": resolvedLaunchNo,
|
|
||||||
if (roomScope || resolvedLaunchNo.isNotEmpty) "scope": "room",
|
|
||||||
};
|
};
|
||||||
const path = "/go/app/voice-room/rocket/reward-records";
|
const path = "/go/app/voice-room/rocket/reward-records";
|
||||||
_rocketApiDebug('request GET $path', query: queryParams);
|
_rocketApiDebug('request GET $path', query: queryParams);
|
||||||
|
|||||||
@ -1,426 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:flutter_cache_manager/flutter_cache_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_status_res.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_network_image_utils.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart';
|
|
||||||
|
|
||||||
const int _roomRocketAssetPreloadMaxConcurrent = 2;
|
|
||||||
const Duration _roomRocketAssetPreloadDelay = Duration(milliseconds: 350);
|
|
||||||
const Duration _roomRocketAssetPreloadTimeout = Duration(seconds: 12);
|
|
||||||
const double _roomRocketAssetPreloadFallbackThreshold = 95;
|
|
||||||
|
|
||||||
enum _RoomRocketPreloadKind { image, pag }
|
|
||||||
|
|
||||||
class _RoomRocketPreloadAsset {
|
|
||||||
const _RoomRocketPreloadAsset({
|
|
||||||
required this.resource,
|
|
||||||
required this.kind,
|
|
||||||
required this.roomId,
|
|
||||||
required this.generation,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String resource;
|
|
||||||
final _RoomRocketPreloadKind kind;
|
|
||||||
final String roomId;
|
|
||||||
final int generation;
|
|
||||||
|
|
||||||
String get key {
|
|
||||||
final uri = _networkUri(resource);
|
|
||||||
return '${kind.name}:${uri?.toString() ?? resource.trim()}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomRocketAssetPreloader {
|
|
||||||
const RoomRocketAssetPreloader._();
|
|
||||||
|
|
||||||
static final Queue<_RoomRocketPreloadAsset> _queue =
|
|
||||||
Queue<_RoomRocketPreloadAsset>();
|
|
||||||
static final Set<String> _scheduledKeys = <String>{};
|
|
||||||
static int _generation = 0;
|
|
||||||
static int _running = 0;
|
|
||||||
static String _activeRoomId = '';
|
|
||||||
|
|
||||||
static void preloadAfterRoomEntry({
|
|
||||||
required String roomId,
|
|
||||||
required SCRoomRocketStatusRes status,
|
|
||||||
}) {
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
if (normalizedRoomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final generation = _syncRoom(normalizedRoomId);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
unawaited(
|
|
||||||
Future<void>.delayed(_roomRocketAssetPreloadDelay, () {
|
|
||||||
if (!_isCurrentRoom(normalizedRoomId, generation)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_enqueueAssets(
|
|
||||||
_assetsForStatus(
|
|
||||||
status,
|
|
||||||
roomId: normalizedRoomId,
|
|
||||||
generation: generation,
|
|
||||||
includeNextLevel: true,
|
|
||||||
includeCurrentRewardCovers: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void preloadForStatus({
|
|
||||||
required String roomId,
|
|
||||||
required SCRoomRocketStatusRes status,
|
|
||||||
}) {
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
if (normalizedRoomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final generation = _syncRoom(normalizedRoomId);
|
|
||||||
_enqueueAssets(
|
|
||||||
_assetsForStatus(
|
|
||||||
status,
|
|
||||||
roomId: normalizedRoomId,
|
|
||||||
generation: generation,
|
|
||||||
includeNextLevel: true,
|
|
||||||
includeCurrentRewardCovers: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void forcePreloadCurrentLaunchAssets({
|
|
||||||
required String roomId,
|
|
||||||
required SCRoomRocketStatusRes status,
|
|
||||||
}) {
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
if (normalizedRoomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final generation = _syncRoom(normalizedRoomId);
|
|
||||||
_enqueueAssets(
|
|
||||||
_assetsForStatus(
|
|
||||||
status,
|
|
||||||
roomId: normalizedRoomId,
|
|
||||||
generation: generation,
|
|
||||||
includeNextLevel: false,
|
|
||||||
includeCurrentRewardCovers: true,
|
|
||||||
onlyCurrentLaunchAssets: true,
|
|
||||||
),
|
|
||||||
priority: true,
|
|
||||||
forceRetry: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cancelCurrentRoom() {
|
|
||||||
_generation += 1;
|
|
||||||
_activeRoomId = '';
|
|
||||||
_queue.clear();
|
|
||||||
_scheduledKeys.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool shouldForceCurrentLaunchPreload(SCRoomRocketStatusRes status) {
|
|
||||||
return _statusPercent(status) >= _forceThreshold(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _syncRoom(String roomId) {
|
|
||||||
if (_activeRoomId != roomId) {
|
|
||||||
_activeRoomId = roomId;
|
|
||||||
_generation += 1;
|
|
||||||
_queue.clear();
|
|
||||||
_scheduledKeys.clear();
|
|
||||||
}
|
|
||||||
return _generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isCurrentRoom(String roomId, int generation) {
|
|
||||||
return _activeRoomId == roomId &&
|
|
||||||
_generation == generation &&
|
|
||||||
_isAppResumed();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isAppResumed() {
|
|
||||||
final state = WidgetsBinding.instance.lifecycleState;
|
|
||||||
return state == null || state == AppLifecycleState.resumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _enqueueAssets(
|
|
||||||
List<_RoomRocketPreloadAsset> assets, {
|
|
||||||
bool priority = false,
|
|
||||||
bool forceRetry = false,
|
|
||||||
}) {
|
|
||||||
if (assets.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final normalizedAssets = priority ? assets.reversed : assets;
|
|
||||||
for (final asset in normalizedAssets) {
|
|
||||||
if (_networkUri(asset.resource) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (forceRetry) {
|
|
||||||
_queue.removeWhere((item) => item.key == asset.key);
|
|
||||||
_scheduledKeys.remove(asset.key);
|
|
||||||
}
|
|
||||||
if (!_scheduledKeys.add(asset.key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (priority) {
|
|
||||||
_queue.addFirst(asset);
|
|
||||||
} else {
|
|
||||||
_queue.addLast(asset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_pump();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _pump() {
|
|
||||||
if (!_isAppResumed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (_running < _roomRocketAssetPreloadMaxConcurrent &&
|
|
||||||
_queue.isNotEmpty) {
|
|
||||||
final asset = _queue.removeFirst();
|
|
||||||
if (!_isCurrentRoom(asset.roomId, asset.generation)) {
|
|
||||||
_scheduledKeys.remove(asset.key);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_running += 1;
|
|
||||||
unawaited(
|
|
||||||
_preloadAsset(asset)
|
|
||||||
.then((ok) {
|
|
||||||
if (!ok) {
|
|
||||||
_scheduledKeys.remove(asset.key);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.whenComplete(() {
|
|
||||||
_running = (_running - 1).clamp(0, 999).toInt();
|
|
||||||
_pump();
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<bool> _preloadAsset(_RoomRocketPreloadAsset asset) async {
|
|
||||||
if (!_isCurrentRoom(asset.roomId, asset.generation)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final uri = _networkUri(asset.resource);
|
|
||||||
if (uri == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final url = uri.toString();
|
|
||||||
try {
|
|
||||||
if (asset.kind == _RoomRocketPreloadKind.pag) {
|
|
||||||
await FileCacheManager.getInstance()
|
|
||||||
.getFile(url: url, headers: buildNetworkImageHeaders(url))
|
|
||||||
.timeout(_roomRocketAssetPreloadTimeout);
|
|
||||||
} else {
|
|
||||||
await DefaultCacheManager()
|
|
||||||
.getSingleFile(url, headers: buildNetworkImageHeaders(url))
|
|
||||||
.timeout(_roomRocketAssetPreloadTimeout);
|
|
||||||
}
|
|
||||||
_debug('preloaded ${asset.kind.name} ${_shortResource(url)}');
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
_debug(
|
|
||||||
'preload failed ${asset.kind.name} ${_shortResource(url)}: $error',
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_RoomRocketPreloadAsset> _assetsForStatus(
|
|
||||||
SCRoomRocketStatusRes status, {
|
|
||||||
required String roomId,
|
|
||||||
required int generation,
|
|
||||||
required bool includeNextLevel,
|
|
||||||
required bool includeCurrentRewardCovers,
|
|
||||||
bool onlyCurrentLaunchAssets = false,
|
|
||||||
}) {
|
|
||||||
final currentLevel = _currentLevel(status);
|
|
||||||
final result = <_RoomRocketPreloadAsset>[];
|
|
||||||
final current = _levelFor(status, currentLevel);
|
|
||||||
if (current != null) {
|
|
||||||
if (!onlyCurrentLaunchAssets) {
|
|
||||||
_addImage(result, current.rocketIconUrl, roomId, generation);
|
|
||||||
}
|
|
||||||
_addPag(result, current.rocketAnimationUrl, roomId, generation);
|
|
||||||
}
|
|
||||||
if (!onlyCurrentLaunchAssets && includeNextLevel) {
|
|
||||||
final next = _levelFor(status, currentLevel + 1);
|
|
||||||
if (next != null) {
|
|
||||||
_addImage(result, next.rocketIconUrl, roomId, generation);
|
|
||||||
_addPag(result, next.rocketAnimationUrl, roomId, generation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (includeCurrentRewardCovers) {
|
|
||||||
final preview = status.rewardPreviewForLevel(currentLevel);
|
|
||||||
for (final reward in preview?.all ?? const <SCRoomRocketRewardRes>[]) {
|
|
||||||
_addImage(result, reward.rewardCover, roomId, generation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _dedupeAssets(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addImage(
|
|
||||||
List<_RoomRocketPreloadAsset> result,
|
|
||||||
String resource,
|
|
||||||
String roomId,
|
|
||||||
int generation,
|
|
||||||
) {
|
|
||||||
final value = resource.trim();
|
|
||||||
if (value.isEmpty || RoomRocketPagEffectOverlay.isPag(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result.add(
|
|
||||||
_RoomRocketPreloadAsset(
|
|
||||||
resource: value,
|
|
||||||
kind: _RoomRocketPreloadKind.image,
|
|
||||||
roomId: roomId,
|
|
||||||
generation: generation,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addPag(
|
|
||||||
List<_RoomRocketPreloadAsset> result,
|
|
||||||
String resource,
|
|
||||||
String roomId,
|
|
||||||
int generation,
|
|
||||||
) {
|
|
||||||
final value = resource.trim();
|
|
||||||
if (!RoomRocketPagEffectOverlay.isPag(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result.add(
|
|
||||||
_RoomRocketPreloadAsset(
|
|
||||||
resource: value,
|
|
||||||
kind: _RoomRocketPreloadKind.pag,
|
|
||||||
roomId: roomId,
|
|
||||||
generation: generation,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_RoomRocketPreloadAsset> _dedupeAssets(
|
|
||||||
List<_RoomRocketPreloadAsset> assets,
|
|
||||||
) {
|
|
||||||
final seen = <String>{};
|
|
||||||
final result = <_RoomRocketPreloadAsset>[];
|
|
||||||
for (final asset in assets) {
|
|
||||||
final key = asset.key;
|
|
||||||
if (!seen.add(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.add(asset);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int _currentLevel(SCRoomRocketStatusRes status) {
|
|
||||||
return (status.currentLevel ?? status.level?.toInt() ?? 1)
|
|
||||||
.clamp(1, 99)
|
|
||||||
.toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRoomRocketLevelRes? _levelFor(SCRoomRocketStatusRes status, int level) {
|
|
||||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
|
||||||
for (final item in status.levels ?? const <SCRoomRocketLevelRes>[]) {
|
|
||||||
if (item.level == normalizedLevel) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
double _statusPercent(SCRoomRocketStatusRes status) {
|
|
||||||
final value = status.displayPercent ?? status.energyPercent;
|
|
||||||
if (value != null) {
|
|
||||||
final raw = value.toDouble();
|
|
||||||
final percent = raw > 0 && raw <= 1 ? raw * 100 : raw;
|
|
||||||
return percent.clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
final currentEnergy = status.currentEnergy ?? 0;
|
|
||||||
final maxEnergy = status.maxEnergy ?? status.needEnergy ?? 0;
|
|
||||||
if (maxEnergy <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ((currentEnergy / maxEnergy) * 100).clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
double _forceThreshold(SCRoomRocketStatusRes status) {
|
|
||||||
final level = _levelFor(status, _currentLevel(status));
|
|
||||||
final threshold = level?.shakeThresholdPercent.toDouble();
|
|
||||||
if (threshold == null || threshold <= 0 || threshold > 100) {
|
|
||||||
return _roomRocketAssetPreloadFallbackThreshold;
|
|
||||||
}
|
|
||||||
return threshold.clamp(80, 99.5).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri? _networkUri(String resource) {
|
|
||||||
final normalized = normalizeImageResourceUrl(resource.trim());
|
|
||||||
if (normalized.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final direct = Uri.tryParse(normalized);
|
|
||||||
if (_isHttpUri(direct)) {
|
|
||||||
return direct;
|
|
||||||
}
|
|
||||||
final encoded = Uri.tryParse(Uri.encodeFull(normalized));
|
|
||||||
if (_isHttpUri(encoded)) {
|
|
||||||
return encoded;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isHttpUri(Uri? uri) {
|
|
||||||
if (uri == null || !uri.hasScheme || uri.host.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final scheme = uri.scheme.toLowerCase();
|
|
||||||
return scheme == 'http' || scheme == 'https';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _shortResource(String value) {
|
|
||||||
final text = value.replaceAll(RegExp(r'\s+'), ' ').trim();
|
|
||||||
if (text.length <= 120) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
return '${text.substring(0, 117)}...';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _debug(String message) {
|
|
||||||
if (!kDebugMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugPrint('[RoomRocketAssetPreloader] $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
List<String> debugRoomRocketAssetPreloadResources(
|
|
||||||
SCRoomRocketStatusRes status, {
|
|
||||||
bool includeNextLevel = true,
|
|
||||||
bool includeCurrentRewardCovers = true,
|
|
||||||
bool onlyCurrentLaunchAssets = false,
|
|
||||||
}) {
|
|
||||||
return _assetsForStatus(
|
|
||||||
status,
|
|
||||||
roomId: 'debug-room',
|
|
||||||
generation: 1,
|
|
||||||
includeNextLevel: includeNextLevel,
|
|
||||||
includeCurrentRewardCovers: includeCurrentRewardCovers,
|
|
||||||
onlyCurrentLaunchAssets: onlyCurrentLaunchAssets,
|
|
||||||
).map((item) => item.resource).toList(growable: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
bool debugShouldForceRoomRocketAssetPreload(SCRoomRocketStatusRes status) {
|
|
||||||
return RoomRocketAssetPreloader.shouldForceCurrentLaunchPreload(status);
|
|
||||||
}
|
|
||||||
@ -49,11 +49,11 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (widget.initialPopupRecords.isNotEmpty) {
|
if (widget.initialPopupRecords.isNotEmpty) {
|
||||||
_appendUniqueRewardRecords(_popupRecords, widget.initialPopupRecords);
|
_popupRecords.addAll(widget.initialPopupRecords);
|
||||||
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
||||||
}
|
}
|
||||||
if (widget.initialRoomRecords.isNotEmpty) {
|
if (widget.initialRoomRecords.isNotEmpty) {
|
||||||
_appendUniqueRewardRecords(_roomRecords, widget.initialRoomRecords);
|
_roomRecords.addAll(widget.initialRoomRecords);
|
||||||
}
|
}
|
||||||
_winners = RoomRocketApiMapper.winnersFromRecords(
|
_winners = RoomRocketApiMapper.winnersFromRecords(
|
||||||
_visibleRoomRewardRecords(),
|
_visibleRoomRewardRecords(),
|
||||||
@ -85,28 +85,34 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
? widget.initialPopupRecords
|
? widget.initialPopupRecords
|
||||||
: await _loadPopupRecords(requestCursor)
|
: await _loadPopupRecords(requestCursor)
|
||||||
: const <SCRoomRocketRewardRecordRes>[];
|
: const <SCRoomRocketRewardRecordRes>[];
|
||||||
if (useInitialPopupRecords) {
|
if (reset && useInitialPopupRecords) {
|
||||||
_usedInitialPopupRecords = true;
|
_usedInitialPopupRecords = true;
|
||||||
}
|
}
|
||||||
final launchNo = _launchNoFromRecords(
|
final nextRoomRecords = await _loadRoomRewardRecords(requestCursor);
|
||||||
nextPopupRecords.isNotEmpty ? nextPopupRecords : _popupRecords,
|
|
||||||
);
|
|
||||||
final nextRoomRecords = await _loadRoomRewardRecords(
|
|
||||||
requestCursor,
|
|
||||||
launchNo: launchNo,
|
|
||||||
);
|
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
_replaceUniqueRewardRecords(_popupRecords, nextPopupRecords);
|
_popupRecords
|
||||||
_replaceUniqueRewardRecords(_roomRecords, [
|
..clear()
|
||||||
|
..addAll(nextPopupRecords);
|
||||||
|
_roomRecords
|
||||||
|
..clear()
|
||||||
|
..addAll(
|
||||||
|
_mergeRewardRecords([
|
||||||
...widget.initialRoomRecords,
|
...widget.initialRoomRecords,
|
||||||
...nextRoomRecords,
|
...nextRoomRecords,
|
||||||
]);
|
]),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
_appendUniqueRewardRecords(_roomRecords, nextRoomRecords);
|
final mergedRoomRecords = _mergeRewardRecords([
|
||||||
|
..._roomRecords,
|
||||||
|
...nextRoomRecords,
|
||||||
|
]);
|
||||||
|
_roomRecords
|
||||||
|
..clear()
|
||||||
|
..addAll(mergedRoomRecords);
|
||||||
}
|
}
|
||||||
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
_rewards = RoomRocketApiMapper.rewardItemsFromRecords(_popupRecords);
|
||||||
_winners = RoomRocketApiMapper.winnersFromRecords(
|
_winners = RoomRocketApiMapper.winnersFromRecords(
|
||||||
@ -158,9 +164,8 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SCRoomRocketRewardRecordRes>> _loadRoomRewardRecords(
|
Future<List<SCRoomRocketRewardRecordRes>> _loadRoomRewardRecords(
|
||||||
int cursor, {
|
int cursor,
|
||||||
String launchNo = '',
|
) async {
|
||||||
}) async {
|
|
||||||
if (widget.roomId.trim().isEmpty) {
|
if (widget.roomId.trim().isEmpty) {
|
||||||
return const <SCRoomRocketRewardRecordRes>[];
|
return const <SCRoomRocketRewardRecordRes>[];
|
||||||
}
|
}
|
||||||
@ -173,8 +178,6 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
widget.roomId,
|
widget.roomId,
|
||||||
cursor: cursor,
|
cursor: cursor,
|
||||||
limit: _rewardDialogPageSize,
|
limit: _rewardDialogPageSize,
|
||||||
launchNo: launchNo,
|
|
||||||
roomScope: true,
|
|
||||||
);
|
);
|
||||||
return res.records;
|
return res.records;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -201,6 +204,20 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
return matched.isEmpty ? _roomRecords : matched;
|
return matched.isEmpty ? _roomRecords : matched;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<SCRoomRocketRewardRecordRes> _mergeRewardRecords(
|
||||||
|
List<SCRoomRocketRewardRecordRes> records,
|
||||||
|
) {
|
||||||
|
final result = <SCRoomRocketRewardRecordRes>[];
|
||||||
|
final seen = <String>{};
|
||||||
|
for (final record in records) {
|
||||||
|
final id = record.id.trim();
|
||||||
|
if (id.isEmpty || seen.add(id)) {
|
||||||
|
result.add(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RoomRocketRewardDialog(
|
return RoomRocketRewardDialog(
|
||||||
@ -217,76 +234,6 @@ class _RoomRocketRewardDialogLoaderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _launchNoFromRecords(List<SCRoomRocketRewardRecordRes> records) {
|
|
||||||
for (final record in records) {
|
|
||||||
final launchNo = record.launchNo.trim();
|
|
||||||
if (launchNo.isNotEmpty) {
|
|
||||||
return launchNo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _replaceUniqueRewardRecords(
|
|
||||||
List<SCRoomRocketRewardRecordRes> target,
|
|
||||||
List<SCRoomRocketRewardRecordRes> records,
|
|
||||||
) {
|
|
||||||
target
|
|
||||||
..clear()
|
|
||||||
..addAll(debugUniqueRoomRocketRewardRecords(records));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _appendUniqueRewardRecords(
|
|
||||||
List<SCRoomRocketRewardRecordRes> target,
|
|
||||||
List<SCRoomRocketRewardRecordRes> records,
|
|
||||||
) {
|
|
||||||
final seen = target.map(_roomRocketRewardRecordUniqueKey).toSet();
|
|
||||||
for (final record in records) {
|
|
||||||
final key = _roomRocketRewardRecordUniqueKey(record);
|
|
||||||
if (!seen.add(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
target.add(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
List<SCRoomRocketRewardRecordRes> debugUniqueRoomRocketRewardRecords(
|
|
||||||
List<SCRoomRocketRewardRecordRes> records,
|
|
||||||
) {
|
|
||||||
final seen = <String>{};
|
|
||||||
final uniqueRecords = <SCRoomRocketRewardRecordRes>[];
|
|
||||||
for (final record in records) {
|
|
||||||
final key = _roomRocketRewardRecordUniqueKey(record);
|
|
||||||
if (!seen.add(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uniqueRecords.add(record);
|
|
||||||
}
|
|
||||||
return uniqueRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _roomRocketRewardRecordUniqueKey(SCRoomRocketRewardRecordRes record) {
|
|
||||||
final id = record.id.trim();
|
|
||||||
if (id.isNotEmpty) {
|
|
||||||
return 'id:$id';
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
'fallback',
|
|
||||||
record.launchNo.trim(),
|
|
||||||
record.roomId.trim(),
|
|
||||||
record.roundNo.toString(),
|
|
||||||
record.level.toString(),
|
|
||||||
record.userId.trim(),
|
|
||||||
record.rewardScene.trim().toUpperCase(),
|
|
||||||
record.rewardType.trim().toUpperCase(),
|
|
||||||
record.rewardItemId.trim(),
|
|
||||||
record.rewardName.trim(),
|
|
||||||
record.rewardAmount.toString(),
|
|
||||||
record.expireDays.toString(),
|
|
||||||
].join('|');
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rewardDialogDebug(String message) {
|
void _rewardDialogDebug(String message) {
|
||||||
if (!kDebugMode) {
|
if (!kDebugMode) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,108 +0,0 @@
|
|||||||
import 'package:flutter_test/flutter_test.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_status_res.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_asset_preloader.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('room rocket asset preloader', () {
|
|
||||||
test('collects current and next level disk preload resources', () {
|
|
||||||
final resources = debugRoomRocketAssetPreloadResources(_status());
|
|
||||||
|
|
||||||
expect(resources, [
|
|
||||||
'https://cdn.example.com/rocket-l2.png',
|
|
||||||
'https://cdn.example.com/rocket-l2.pag',
|
|
||||||
'https://cdn.example.com/rocket-l3.png',
|
|
||||||
'https://cdn.example.com/rocket-l3.pag',
|
|
||||||
'https://cdn.example.com/reward-top1.png',
|
|
||||||
'https://cdn.example.com/reward-in-room.png',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('force mode only collects current launch pag and reward covers', () {
|
|
||||||
final resources = debugRoomRocketAssetPreloadResources(
|
|
||||||
_status(),
|
|
||||||
includeNextLevel: false,
|
|
||||||
onlyCurrentLaunchAssets: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(resources, [
|
|
||||||
'https://cdn.example.com/rocket-l2.pag',
|
|
||||||
'https://cdn.example.com/reward-top1.png',
|
|
||||||
'https://cdn.example.com/reward-in-room.png',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('uses shake threshold to decide force preload', () {
|
|
||||||
expect(
|
|
||||||
debugShouldForceRoomRocketAssetPreload(_status(percent: 94)),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
debugShouldForceRoomRocketAssetPreload(_status(percent: 95)),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRoomRocketStatusRes _status({num percent = 95}) {
|
|
||||||
return SCRoomRocketStatusRes(
|
|
||||||
roomId: 'room-1',
|
|
||||||
currentLevel: 2,
|
|
||||||
displayPercent: percent,
|
|
||||||
levels: [
|
|
||||||
_level(
|
|
||||||
level: 2,
|
|
||||||
icon: 'https://cdn.example.com/rocket-l2.png',
|
|
||||||
animation: 'https://cdn.example.com/rocket-l2.pag',
|
|
||||||
rewardPreview: SCRoomRocketRewardPreviewRes(
|
|
||||||
top1: [
|
|
||||||
_reward('https://cdn.example.com/reward-top1.png'),
|
|
||||||
_reward('https://cdn.example.com/reward-top1.png'),
|
|
||||||
],
|
|
||||||
ignite: const <SCRoomRocketRewardRes>[],
|
|
||||||
inRoom: [_reward('https://cdn.example.com/reward-in-room.png')],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_level(
|
|
||||||
level: 3,
|
|
||||||
icon: 'https://cdn.example.com/rocket-l3.png',
|
|
||||||
animation: 'https://cdn.example.com/rocket-l3.pag',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRoomRocketLevelRes _level({
|
|
||||||
required int level,
|
|
||||||
required String icon,
|
|
||||||
required String animation,
|
|
||||||
SCRoomRocketRewardPreviewRes? rewardPreview,
|
|
||||||
}) {
|
|
||||||
return SCRoomRocketLevelRes(
|
|
||||||
id: level.toString(),
|
|
||||||
level: level,
|
|
||||||
needEnergy: level * 100,
|
|
||||||
rocketIconUrl: icon,
|
|
||||||
rocketPreviewUrl: '',
|
|
||||||
rocketAnimationUrl: animation,
|
|
||||||
progressBarUrl: '',
|
|
||||||
shakeThresholdPercent: 95,
|
|
||||||
enabled: true,
|
|
||||||
sort: level,
|
|
||||||
rewardPreview: rewardPreview,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRoomRocketRewardRes _reward(String cover) {
|
|
||||||
return SCRoomRocketRewardRes(
|
|
||||||
rewardScene: 'TOP1',
|
|
||||||
rewardType: 'GOLD',
|
|
||||||
rewardItemId: '',
|
|
||||||
rewardName: 'Yumi',
|
|
||||||
rewardCover: cover,
|
|
||||||
rocketIconUrl: '',
|
|
||||||
rewardAmount: 1000,
|
|
||||||
expireDays: 0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
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_reward_dialog_loader.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('room rocket reward dialog loader', () {
|
|
||||||
test('deduplicates reward records by id', () {
|
|
||||||
final records = debugUniqueRoomRocketRewardRecords([
|
|
||||||
_rewardRecord(id: 'record-1', rewardAmount: 5000),
|
|
||||||
_rewardRecord(id: 'record-1', rewardAmount: 5200),
|
|
||||||
_rewardRecord(id: 'record-2', rewardAmount: 5200),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(records, hasLength(2));
|
|
||||||
expect(records.map((item) => item.id), ['record-1', 'record-2']);
|
|
||||||
expect(records.first.rewardAmount, 5000);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('deduplicates id-less reward records by reward identity', () {
|
|
||||||
final records = debugUniqueRoomRocketRewardRecords([
|
|
||||||
_rewardRecord(id: '', rewardAmount: 5000),
|
|
||||||
_rewardRecord(id: '', rewardAmount: 5000),
|
|
||||||
_rewardRecord(id: '', rewardAmount: 5200),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(records, hasLength(2));
|
|
||||||
expect(records.map((item) => item.rewardAmount), [5000, 5200]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SCRoomRocketRewardRecordRes _rewardRecord({
|
|
||||||
required String id,
|
|
||||||
required int rewardAmount,
|
|
||||||
}) {
|
|
||||||
return SCRoomRocketRewardRecordRes.fromJson({
|
|
||||||
'id': id,
|
|
||||||
'launchNo': 'launch-1',
|
|
||||||
'roomId': 'room-1',
|
|
||||||
'dayKey': '2026-05-15',
|
|
||||||
'roundNo': 1,
|
|
||||||
'level': 2,
|
|
||||||
'userId': 'user-1',
|
|
||||||
'userNickname': 'User 1',
|
|
||||||
'rewardScene': 'TOP1',
|
|
||||||
'rewardType': 'GOLD',
|
|
||||||
'rewardItemId': 'gold',
|
|
||||||
'rewardName': 'Yumi',
|
|
||||||
'rewardCover': '',
|
|
||||||
'rewardAmount': rewardAmount,
|
|
||||||
'expireDays': 0,
|
|
||||||
'grantStatus': 'SUCCESS',
|
|
||||||
'popupStatus': 'UNREAD',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user