Compare commits
No commits in common. "d5653afa4f14bff8a261199f83940275590779ad" and "660f712ae6817adb6f7d94cca11c6c5a4e2b957b" have entirely different histories.
d5653afa4f
...
660f712ae6
@ -280,8 +280,8 @@
|
|||||||
"bDLeaderInviteYouToBecomeBDLeader": "ندعوك لتصبح قائد تطوير الأعمال",
|
"bDLeaderInviteYouToBecomeBDLeader": "ندعوك لتصبح قائد تطوير الأعمال",
|
||||||
"win2": "فوز {1}",
|
"win2": "فوز {1}",
|
||||||
"level": "مستوى",
|
"level": "مستوى",
|
||||||
"wealthLevel": "مستوى الثراء",
|
"wealthLevel": "مستوى الثروة",
|
||||||
"userLevel": "مستوى الجاذبية",
|
"userLevel": "مستوى المستخدم",
|
||||||
"themeGoToUploadTips": "1. ستتم مراجعة التحميل خلال 24 ساعة بعد نجاحه.\n2. ستُعاد جميع العملات إذا فشلت المراجعة.",
|
"themeGoToUploadTips": "1. ستتم مراجعة التحميل خلال 24 ساعة بعد نجاحه.\n2. ستُعاد جميع العملات إذا فشلت المراجعة.",
|
||||||
"pleaseUploadAccordingToExample": "يرجى الرفع وفقًا للمثال المعروض.",
|
"pleaseUploadAccordingToExample": "يرجى الرفع وفقًا للمثال المعروض.",
|
||||||
"goToUpload": "الانتقال للرفع",
|
"goToUpload": "الانتقال للرفع",
|
||||||
@ -665,10 +665,8 @@
|
|||||||
"mountains": "مركبات",
|
"mountains": "مركبات",
|
||||||
"buy": "شراء",
|
"buy": "شراء",
|
||||||
"visitorList": "قائمة الزوار",
|
"visitorList": "قائمة الزوار",
|
||||||
"spendCoinsToGainExperiencePoints": "اصرف العملات لكسب نقاط الخبرة",
|
"spendCoinsToGainExperiencePoints": "اصرف العملات لكسب نقاط الخبرة",
|
||||||
"richLevelUpgradeAnswer": "دع ثروتك تغزو كل شيء",
|
"howToUpgrade": "كيف يمكن الترقية؟",
|
||||||
"charmLevelUpgradeAnswer": "اجعل الآخرين يخضعون لسحرك",
|
|
||||||
"howToUpgrade": "كيف يمكن الترقية؟",
|
|
||||||
"higherLevelFancierAvatarFrame": "مستوى أعلى، شارات/إطار صور أفخم.",
|
"higherLevelFancierAvatarFrame": "مستوى أعلى، شارات/إطار صور أفخم.",
|
||||||
"use": "استخدم",
|
"use": "استخدم",
|
||||||
"expired": "منتهي",
|
"expired": "منتهي",
|
||||||
|
|||||||
@ -267,7 +267,7 @@
|
|||||||
"aboutUs": "আমাদের সম্পর্কে",
|
"aboutUs": "আমাদের সম্পর্কে",
|
||||||
"theme": "থিম",
|
"theme": "থিম",
|
||||||
"wealthLevel": "সম্পদ স্তর",
|
"wealthLevel": "সম্পদ স্তর",
|
||||||
"userLevel": "আকর্ষণ স্তর",
|
"userLevel": "ব্যবহারকারী স্তর",
|
||||||
"goToUpload": "আপলোড করতে যান",
|
"goToUpload": "আপলোড করতে যান",
|
||||||
"logout": "লগআউট",
|
"logout": "লগআউট",
|
||||||
"luck": "ভাগ্য",
|
"luck": "ভাগ্য",
|
||||||
@ -600,8 +600,6 @@
|
|||||||
"toConsume": "খরচ করার জন্য",
|
"toConsume": "খরচ করার জন্য",
|
||||||
"howToUpgrade": "কীভাবে উন্নত করব?",
|
"howToUpgrade": "কীভাবে উন্নত করব?",
|
||||||
"spendCoinsToGainExperiencePoints": "অভিজ্ঞতা পয়েন্ট পাওয়ার জন্য কয়েন খরচ করুন",
|
"spendCoinsToGainExperiencePoints": "অভিজ্ঞতা পয়েন্ট পাওয়ার জন্য কয়েন খরচ করুন",
|
||||||
"richLevelUpgradeAnswer": "তোমার সম্পদ সবকিছু জয় করুক",
|
|
||||||
"charmLevelUpgradeAnswer": "অন্যদের তোমার আকর্ষণের কাছে নত করো",
|
|
||||||
"higherLevelFancierAvatarFrame": "উচ্চতর স্তর, আরও সুন্দর ব্যাজ/প্রোফাইল ফ্রেম",
|
"higherLevelFancierAvatarFrame": "উচ্চতর স্তর, আরও সুন্দর ব্যাজ/প্রোফাইল ফ্রেম",
|
||||||
"all": "সব",
|
"all": "সব",
|
||||||
"gift": "গিফট",
|
"gift": "গিফট",
|
||||||
|
|||||||
@ -236,8 +236,8 @@
|
|||||||
"about": "About",
|
"about": "About",
|
||||||
"aboutUs": "About Us",
|
"aboutUs": "About Us",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
"wealthLevel": "Rich Level",
|
"wealthLevel": "Wealth Level",
|
||||||
"userLevel": "Charm Level",
|
"userLevel": "User Level",
|
||||||
"goToUpload": "Go to upload",
|
"goToUpload": "Go to upload",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"luck": "Luck",
|
"luck": "Luck",
|
||||||
@ -569,8 +569,6 @@
|
|||||||
"toConsume": "To consume",
|
"toConsume": "To consume",
|
||||||
"howToUpgrade": "How to upgrade?",
|
"howToUpgrade": "How to upgrade?",
|
||||||
"spendCoinsToGainExperiencePoints": "Spend Coins to gain experience points",
|
"spendCoinsToGainExperiencePoints": "Spend Coins to gain experience points",
|
||||||
"richLevelUpgradeAnswer": "Let your wealth conquer everything",
|
|
||||||
"charmLevelUpgradeAnswer": "Make others submit to your charm",
|
|
||||||
"higherLevelFancierAvatarFrame": "Higher level, fancier badges/avatar frame",
|
"higherLevelFancierAvatarFrame": "Higher level, fancier badges/avatar frame",
|
||||||
"all": "All",
|
"all": "All",
|
||||||
"gift": "Gift",
|
"gift": "Gift",
|
||||||
@ -726,7 +724,7 @@
|
|||||||
"roomRocketNoteTitle": "Note",
|
"roomRocketNoteTitle": "Note",
|
||||||
"roomRocketNoteSectionTitle": "Each time a VIP",
|
"roomRocketNoteSectionTitle": "Each time a VIP",
|
||||||
"roomRocketNoteSectionBody": "1. Each time a VIP level is purchased, it is valid for 30 days, and can be manually renewed after expiration.",
|
"roomRocketNoteSectionBody": "1. Each time a VIP level is purchased, it is valid for 30 days, and can be manually renewed after expiration.",
|
||||||
"roomRocketRewardEmptyText": "No reward",
|
"roomRocketRewardEmptyText": "No rewards earned. Better luck next time.",
|
||||||
"roomRocketRecordEmptyText": "No reward records",
|
"roomRocketRecordEmptyText": "No reward records",
|
||||||
"launchRocket": "Launch Rocket",
|
"launchRocket": "Launch Rocket",
|
||||||
"roomRocketFuelFullNotice": "Rocket fuel is full! Launching in {1}s!"
|
"roomRocketFuelFullNotice": "Rocket fuel is full! Launching in {1}s!"
|
||||||
|
|||||||
@ -237,7 +237,7 @@
|
|||||||
"aboutUs": "À propos de nous",
|
"aboutUs": "À propos de nous",
|
||||||
"theme": "Thème",
|
"theme": "Thème",
|
||||||
"wealthLevel": "Niveau de richesse",
|
"wealthLevel": "Niveau de richesse",
|
||||||
"userLevel": "Niveau de charme",
|
"userLevel": "Niveau utilisateur",
|
||||||
"goToUpload": "Aller au téléchargement",
|
"goToUpload": "Aller au téléchargement",
|
||||||
"logout": "Déconnexion",
|
"logout": "Déconnexion",
|
||||||
"luck": "Chance",
|
"luck": "Chance",
|
||||||
@ -569,8 +569,6 @@
|
|||||||
"toConsume": "Consommer",
|
"toConsume": "Consommer",
|
||||||
"howToUpgrade": "Comment mettre à niveau ?",
|
"howToUpgrade": "Comment mettre à niveau ?",
|
||||||
"spendCoinsToGainExperiencePoints": "Dépensez des pièces pour gagner des points d'expérience",
|
"spendCoinsToGainExperiencePoints": "Dépensez des pièces pour gagner des points d'expérience",
|
||||||
"richLevelUpgradeAnswer": "Que ta richesse conquière tout",
|
|
||||||
"charmLevelUpgradeAnswer": "Fais s'incliner les autres devant ton charme",
|
|
||||||
"higherLevelFancierAvatarFrame": "Cadre de badges/avatar de niveau supérieur et plus sophistiqué",
|
"higherLevelFancierAvatarFrame": "Cadre de badges/avatar de niveau supérieur et plus sophistiqué",
|
||||||
"all": "Tout",
|
"all": "Tout",
|
||||||
"gift": "Cadeau",
|
"gift": "Cadeau",
|
||||||
|
|||||||
@ -237,7 +237,7 @@
|
|||||||
"aboutUs": "Chi siamo",
|
"aboutUs": "Chi siamo",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"wealthLevel": "Livello di ricchezza",
|
"wealthLevel": "Livello di ricchezza",
|
||||||
"userLevel": "Livello di fascino",
|
"userLevel": "Livello utente",
|
||||||
"goToUpload": "Vai al caricamento",
|
"goToUpload": "Vai al caricamento",
|
||||||
"logout": "Esci",
|
"logout": "Esci",
|
||||||
"luck": "Fortuna",
|
"luck": "Fortuna",
|
||||||
@ -569,8 +569,6 @@
|
|||||||
"toConsume": "Consumare",
|
"toConsume": "Consumare",
|
||||||
"howToUpgrade": "Come aggiornare?",
|
"howToUpgrade": "Come aggiornare?",
|
||||||
"spendCoinsToGainExperiencePoints": "Spendi monete per guadagnare punti esperienza",
|
"spendCoinsToGainExperiencePoints": "Spendi monete per guadagnare punti esperienza",
|
||||||
"richLevelUpgradeAnswer": "Lascia che la tua ricchezza conquisti tutto",
|
|
||||||
"charmLevelUpgradeAnswer": "Fa' che gli altri si arrendano al tuo fascino",
|
|
||||||
"higherLevelFancierAvatarFrame": "Distintivi/cornice avatar di livello superiore e più elaborati",
|
"higherLevelFancierAvatarFrame": "Distintivi/cornice avatar di livello superiore e più elaborati",
|
||||||
"all": "Tutto",
|
"all": "Tutto",
|
||||||
"gift": "Regalo",
|
"gift": "Regalo",
|
||||||
|
|||||||
@ -237,7 +237,7 @@
|
|||||||
"aboutUs": "Sobre nós",
|
"aboutUs": "Sobre nós",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"wealthLevel": "Nível de riqueza",
|
"wealthLevel": "Nível de riqueza",
|
||||||
"userLevel": "Nível de charme",
|
"userLevel": "Nível de usuário",
|
||||||
"goToUpload": "Ir para carregar",
|
"goToUpload": "Ir para carregar",
|
||||||
"logout": "Sair",
|
"logout": "Sair",
|
||||||
"luck": "Sorte",
|
"luck": "Sorte",
|
||||||
@ -569,8 +569,6 @@
|
|||||||
"toConsume": "Para consumir",
|
"toConsume": "Para consumir",
|
||||||
"howToUpgrade": "Como atualizar?",
|
"howToUpgrade": "Como atualizar?",
|
||||||
"spendCoinsToGainExperiencePoints": "Gaste moedas para ganhar pontos de experiência",
|
"spendCoinsToGainExperiencePoints": "Gaste moedas para ganhar pontos de experiência",
|
||||||
"richLevelUpgradeAnswer": "Deixe sua riqueza conquistar tudo",
|
|
||||||
"charmLevelUpgradeAnswer": "Faça os outros se renderem ao seu charme",
|
|
||||||
"higherLevelFancierAvatarFrame": "Emblemas/moldura de avatar de nível superior e mais sofisticados",
|
"higherLevelFancierAvatarFrame": "Emblemas/moldura de avatar de nível superior e mais sofisticados",
|
||||||
"all": "Todos",
|
"all": "Todos",
|
||||||
"gift": "Presente",
|
"gift": "Presente",
|
||||||
|
|||||||
@ -225,8 +225,8 @@
|
|||||||
"about": "Hakkında",
|
"about": "Hakkında",
|
||||||
"aboutUs": "Hakkımızda",
|
"aboutUs": "Hakkımızda",
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"wealthLevel": "Zenginlik Seviyesi",
|
"wealthLevel": "Servet Seviyesi",
|
||||||
"userLevel": "Çekicilik Seviyesi",
|
"userLevel": "Kullanıcı Seviyesi",
|
||||||
"goToUpload": "Yüklemeye Git",
|
"goToUpload": "Yüklemeye Git",
|
||||||
"logout": "Çıkış Yap",
|
"logout": "Çıkış Yap",
|
||||||
"luck": "Şans",
|
"luck": "Şans",
|
||||||
@ -566,12 +566,10 @@
|
|||||||
"obtain": "Al",
|
"obtain": "Al",
|
||||||
"win2": "{1} Kazan",
|
"win2": "{1} Kazan",
|
||||||
"backTheRoom": "Odaya Dön",
|
"backTheRoom": "Odaya Dön",
|
||||||
"toConsume": "Tüketmek İçin",
|
"toConsume": "Tüketmek İçin",
|
||||||
"howToUpgrade": "Nasıl yükseltirim?",
|
"howToUpgrade": "Nasıl yükseltirim?",
|
||||||
"spendCoinsToGainExperiencePoints": "Deneyim puanı kazanmak için jetton harcayın",
|
"spendCoinsToGainExperiencePoints": "Deneyim puanı kazanmak için jetton harcayın",
|
||||||
"richLevelUpgradeAnswer": "Servetin her şeyi fethetsin",
|
"higherLevelFancierAvatarFrame": "Daha yüksek seviye, daha şık rozetler/profil çerçevesi",
|
||||||
"charmLevelUpgradeAnswer": "Başkalarını cazibene boyun eğdir",
|
|
||||||
"higherLevelFancierAvatarFrame": "Daha yüksek seviye, daha şık rozetler/profil çerçevesi",
|
|
||||||
"all": "Tümü",
|
"all": "Tümü",
|
||||||
"gift": "Hediye",
|
"gift": "Hediye",
|
||||||
"chat": "Sohbet",
|
"chat": "Sohbet",
|
||||||
|
|||||||
@ -79,10 +79,6 @@ class SCRoomMsgType {
|
|||||||
///用户火箭中奖
|
///用户火箭中奖
|
||||||
static const String rocketRewardUser = "ROCKET_REWARD_USER";
|
static const String rocketRewardUser = "ROCKET_REWARD_USER";
|
||||||
|
|
||||||
///语音房火箭用户中奖
|
|
||||||
static const String voiceRoomRocketRewardUser =
|
|
||||||
"VOICE_ROOM_ROCKET_REWARD_USER";
|
|
||||||
|
|
||||||
///火箭燃料满倒计时发射提示
|
///火箭燃料满倒计时发射提示
|
||||||
static const String rocketLaunchNotice = "ROCKET_LAUNCH_NOTICE";
|
static const String rocketLaunchNotice = "ROCKET_LAUNCH_NOTICE";
|
||||||
|
|
||||||
@ -133,8 +129,4 @@ class SCRoomMsgType {
|
|||||||
static const String gameBurstCrystalSprint = "GAME_BURST_CRYSTAL_SPRINT";
|
static const String gameBurstCrystalSprint = "GAME_BURST_CRYSTAL_SPRINT";
|
||||||
static const String gameBurstCrystalBox = "GAME_BURST_CRYSTAL_BOX";
|
static const String gameBurstCrystalBox = "GAME_BURST_CRYSTAL_BOX";
|
||||||
static const String refreshOnlineUser = "REFRESH_ONLINE_USER";
|
static const String refreshOnlineUser = "REFRESH_ONLINE_USER";
|
||||||
|
|
||||||
static bool isRocketRewardUserType(String? type) {
|
|
||||||
return type == rocketRewardUser || type == voiceRoomRocketRewardUser;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1042,14 +1042,10 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get backTheRoom => translate('backTheRoom');
|
String get backTheRoom => translate('backTheRoom');
|
||||||
|
|
||||||
String get spendCoinsToGainExperiencePoints =>
|
String get spendCoinsToGainExperiencePoints =>
|
||||||
translate('spendCoinsToGainExperiencePoints');
|
translate('spendCoinsToGainExperiencePoints');
|
||||||
|
|
||||||
String get richLevelUpgradeAnswer => translate('richLevelUpgradeAnswer');
|
String get enterRoomName => translate('enterRoomName');
|
||||||
|
|
||||||
String get charmLevelUpgradeAnswer => translate('charmLevelUpgradeAnswer');
|
|
||||||
|
|
||||||
String get enterRoomName => translate('enterRoomName');
|
|
||||||
|
|
||||||
String get selectYourCountry => translate('selectYourCountry');
|
String get selectYourCountry => translate('selectYourCountry');
|
||||||
|
|
||||||
|
|||||||
@ -560,6 +560,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
///礼物上飘动画
|
///礼物上飘动画
|
||||||
_floatingGiftListener(Msg msg) {
|
_floatingGiftListener(Msg msg) {
|
||||||
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||||||
|
rtcProvider.scheduleRoomRocketStatusRefreshForGift();
|
||||||
if (!rtcProvider.shouldShowRoomVisualEffects) {
|
if (!rtcProvider.shouldShowRoomVisualEffects) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,13 @@ class _StoreChatboxPageState
|
|||||||
height: 45.w,
|
height: 45.w,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.w),
|
SizedBox(height: 3.w),
|
||||||
|
buildStoreBagItemTitle(
|
||||||
|
res.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 11.sp,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.w),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -87,7 +87,13 @@ class StoreDataCardPageState
|
|||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.w),
|
SizedBox(height: 8.w),
|
||||||
|
buildStoreBagItemTitle(
|
||||||
|
res.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 11.sp,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.w),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -87,7 +87,13 @@ class _StoreHeaddressPageState
|
|||||||
width: 55.w,
|
width: 55.w,
|
||||||
height: 55.w,
|
height: 55.w,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.w),
|
SizedBox(height: 3.w),
|
||||||
|
buildStoreBagItemTitle(
|
||||||
|
res.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 11.sp,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.w),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -91,7 +91,13 @@ class _StoreMountainsPageState
|
|||||||
width: 55.w,
|
width: 55.w,
|
||||||
height: 55.w,
|
height: 55.w,
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.w),
|
SizedBox(height: 3.w),
|
||||||
|
buildStoreBagItemTitle(
|
||||||
|
res.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 11.sp,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.w),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -91,7 +91,13 @@ class _StoreThemePagePageState
|
|||||||
height: 55.w,
|
height: 55.w,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.w),
|
SizedBox(height: 3.w),
|
||||||
|
buildStoreBagItemTitle(
|
||||||
|
res.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 11.sp,
|
||||||
|
),
|
||||||
|
SizedBox(height: 3.w),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -110,26 +110,18 @@ class _UserLevelPageState extends State<UserLevelPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 185.w,
|
width: 185.w,
|
||||||
height: 6.w,
|
height: 6.w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: Colors.white24,
|
color: Colors.white24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
],
|
||||||
width: getLevelExperience(185.w),
|
),
|
||||||
height: 6.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
color: SocialChatTheme.primaryLight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 5.w),
|
SizedBox(height: 5.w),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -158,7 +150,19 @@ class _UserLevelPageState extends State<UserLevelPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Image.asset("sc_images/level/sc_icon_user_level_center_bg_1.png"),
|
text(
|
||||||
|
SCAppLocalizations.of(context)!.medalAndAvatarFrameRewards,
|
||||||
|
fontSize: 20.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
textColor: SocialChatTheme.primaryLight,
|
||||||
|
),
|
||||||
|
text(
|
||||||
|
SCAppLocalizations.of(context)!.higherLevelFancierAvatarFrame,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
textColor: Colors.white,
|
||||||
|
),
|
||||||
|
Image.asset("sc_images/level/sc_icon_user_level_center_bg_1.png"),
|
||||||
text(
|
text(
|
||||||
SCAppLocalizations.of(context)!.howToUpgrade,
|
SCAppLocalizations.of(context)!.howToUpgrade,
|
||||||
fontSize: 20.sp,
|
fontSize: 20.sp,
|
||||||
@ -166,15 +170,14 @@ class _UserLevelPageState extends State<UserLevelPage> {
|
|||||||
textColor: SocialChatTheme.primaryLight,
|
textColor: SocialChatTheme.primaryLight,
|
||||||
),
|
),
|
||||||
SizedBox(height: 3),
|
SizedBox(height: 3),
|
||||||
text(
|
text(
|
||||||
SCAppLocalizations.of(context)!.charmLevelUpgradeAnswer,
|
SCAppLocalizations.of(
|
||||||
fontSize: 12.sp,
|
context,
|
||||||
fontWeight: FontWeight.w600,
|
)!.spendCoinsToGainExperiencePoints,
|
||||||
textColor: Colors.white,
|
fontSize: 12.sp,
|
||||||
maxLines: 2,
|
fontWeight: FontWeight.w600,
|
||||||
maxWidth: 320.w,
|
textColor: Colors.white,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
|
||||||
SizedBox(height: 25.w),
|
SizedBox(height: 25.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -202,15 +205,12 @@ class _UserLevelPageState extends State<UserLevelPage> {
|
|||||||
: Container();
|
: Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
double getLevelExperience(double width) {
|
double getLevelExperience(double width) {
|
||||||
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
||||||
int nexExp = SCStringUtils.convertToInteger(res?.nextExperience ?? "0");
|
int nexExp = SCStringUtils.convertToInteger(res?.nextExperience ?? "0");
|
||||||
int levelExp = thatExp + nexExp;
|
int levelExp = thatExp + nexExp;
|
||||||
if (levelExp <= 0) {
|
return (thatExp / levelExp) * width;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
return (thatExp / levelExp) * width;
|
|
||||||
}
|
|
||||||
|
|
||||||
num getLevelTotalExperience() {
|
num getLevelTotalExperience() {
|
||||||
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
||||||
|
|||||||
@ -110,26 +110,18 @@ class _WealthLevelPageState extends State<WealthLevelPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 185.w,
|
width: 185.w,
|
||||||
height: 6.w,
|
height: 6.w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: Colors.white24,
|
color: Colors.white24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
],
|
||||||
width: getLevelExperience(185.w),
|
),
|
||||||
height: 6.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
color: SocialChatTheme.primaryLight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: 5.w),
|
SizedBox(height: 5.w),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -158,7 +150,19 @@ class _WealthLevelPageState extends State<WealthLevelPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Image.asset("sc_images/level/sc_icon_user_wealth_center_bg_1.png"),
|
text(
|
||||||
|
SCAppLocalizations.of(context)!.medalAndAvatarFrameRewards,
|
||||||
|
fontSize: 20.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
textColor: SocialChatTheme.primaryLight,
|
||||||
|
),
|
||||||
|
text(
|
||||||
|
SCAppLocalizations.of(context)!.higherLevelFancierAvatarFrame,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
textColor: Colors.white,
|
||||||
|
),
|
||||||
|
Image.asset("sc_images/level/sc_icon_user_wealth_center_bg_1.png"),
|
||||||
text(
|
text(
|
||||||
SCAppLocalizations.of(context)!.howToUpgrade,
|
SCAppLocalizations.of(context)!.howToUpgrade,
|
||||||
fontSize: 20.sp,
|
fontSize: 20.sp,
|
||||||
@ -166,15 +170,14 @@ class _WealthLevelPageState extends State<WealthLevelPage> {
|
|||||||
textColor: SocialChatTheme.primaryLight,
|
textColor: SocialChatTheme.primaryLight,
|
||||||
),
|
),
|
||||||
SizedBox(height: 3),
|
SizedBox(height: 3),
|
||||||
text(
|
text(
|
||||||
SCAppLocalizations.of(context)!.richLevelUpgradeAnswer,
|
SCAppLocalizations.of(
|
||||||
fontSize: 12.sp,
|
context,
|
||||||
fontWeight: FontWeight.w600,
|
)!.spendCoinsToGainExperiencePoints,
|
||||||
textColor: Colors.white,
|
fontSize: 12.sp,
|
||||||
maxLines: 2,
|
fontWeight: FontWeight.w600,
|
||||||
maxWidth: 320.w,
|
textColor: Colors.white,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
),
|
|
||||||
SizedBox(height: 25.w),
|
SizedBox(height: 25.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -202,15 +205,12 @@ class _WealthLevelPageState extends State<WealthLevelPage> {
|
|||||||
: Container();
|
: Container();
|
||||||
}
|
}
|
||||||
|
|
||||||
double getLevelExperience(double width) {
|
double getLevelExperience(double width) {
|
||||||
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
||||||
int nexExp = SCStringUtils.convertToInteger(res?.nextExperience ?? "0");
|
int nexExp = SCStringUtils.convertToInteger(res?.nextExperience ?? "0");
|
||||||
int levelExp = thatExp + nexExp;
|
int levelExp = thatExp + nexExp;
|
||||||
if (levelExp <= 0) {
|
return (thatExp / levelExp) * width;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
return (thatExp / levelExp) * width;
|
|
||||||
}
|
|
||||||
|
|
||||||
num getLevelTotalExperience() {
|
num getLevelTotalExperience() {
|
||||||
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
int thatExp = SCStringUtils.convertToInteger(res?.thatExperience ?? "0");
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import 'package:yumi/services/audio/rtm_manager.dart';
|
|||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/badge/sc_user_badge_strip.dart';
|
import 'package:yumi/ui_kit/widgets/badge/sc_user_badge_strip.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
|
||||||
|
|
||||||
import '../../../app/constants/sc_screen.dart';
|
import '../../../app/constants/sc_screen.dart';
|
||||||
import '../../../shared/business_logic/models/res/sc_user_counter_res.dart';
|
import '../../../shared/business_logic/models/res/sc_user_counter_res.dart';
|
||||||
@ -579,8 +580,30 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProfileDataCardBackground(PropsResources? _) {
|
Widget _buildProfileDataCardBackground(PropsResources? dataCard) {
|
||||||
return const ColoredBox(color: _profileBg);
|
if (dataCard == null) {
|
||||||
|
return const ColoredBox(color: _profileBg);
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
SCDataCardResourceView(resource: dataCard, fit: BoxFit.cover),
|
||||||
|
DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.black.withValues(alpha: 0.26),
|
||||||
|
_profileBg.withValues(alpha: 0.54),
|
||||||
|
_profileBg.withValues(alpha: 0.78),
|
||||||
|
],
|
||||||
|
stops: const [0, 0.45, 1],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropsResources? _activeDataCard(SocialChatUserProfile? profile) {
|
PropsResources? _activeDataCard(SocialChatUserProfile? profile) {
|
||||||
|
|||||||
@ -151,7 +151,7 @@ class _ProfileWallOfHonorsPageState extends State<ProfileWallOfHonorsPage> {
|
|||||||
),
|
),
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
mainAxisExtent: 96.w,
|
mainAxisExtent: 132.w,
|
||||||
mainAxisSpacing: 14.w,
|
mainAxisSpacing: 14.w,
|
||||||
crossAxisSpacing: 12.w,
|
crossAxisSpacing: 12.w,
|
||||||
),
|
),
|
||||||
@ -161,26 +161,39 @@ class _ProfileWallOfHonorsPageState extends State<ProfileWallOfHonorsPage> {
|
|||||||
|
|
||||||
Widget _buildBadgeItem(WearBadge badge) {
|
Widget _buildBadgeItem(WearBadge badge) {
|
||||||
final url = _badgeUrl(badge);
|
final url = _badgeUrl(badge);
|
||||||
return Center(
|
final name = (badge.badgeName ?? "").trim();
|
||||||
child: Container(
|
return Column(
|
||||||
width: 86.w,
|
mainAxisSize: MainAxisSize.min,
|
||||||
height: 86.w,
|
children: [
|
||||||
alignment: Alignment.center,
|
Container(
|
||||||
decoration: BoxDecoration(
|
width: 86.w,
|
||||||
color: const Color(0xff0E332D),
|
height: 86.w,
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
alignment: Alignment.center,
|
||||||
border: Border.all(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xff18F2B1).withValues(alpha: 0.24),
|
color: const Color(0xff0E332D),
|
||||||
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xff18F2B1).withValues(alpha: 0.24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: netImage(
|
||||||
|
url: url,
|
||||||
|
width: 68.w,
|
||||||
|
height: 68.w,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
noDefaultImg: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: netImage(
|
SizedBox(height: 8.w),
|
||||||
url: url,
|
text(
|
||||||
width: 68.w,
|
name.isEmpty ? "--" : name,
|
||||||
height: 68.w,
|
fontSize: 12,
|
||||||
fit: BoxFit.contain,
|
textColor: Colors.white,
|
||||||
noDefaultImg: true,
|
fontWeight: FontWeight.w400,
|
||||||
|
maxLines: 2,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
|||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
|
|
||||||
|
|
||||||
class VipDetailPage extends StatefulWidget {
|
class VipDetailPage extends StatefulWidget {
|
||||||
const VipDetailPage({super.key});
|
const VipDetailPage({super.key});
|
||||||
@ -59,6 +58,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
final status = results[1] as SCVipStatusRes;
|
final status = results[1] as SCVipStatusRes;
|
||||||
final resolvedStatus = _resolveStatus(status, home.state);
|
final resolvedStatus = _resolveStatus(status, home.state);
|
||||||
final selectedLevel = _resolveInitialLevel(home, resolvedStatus);
|
final selectedLevel = _resolveInitialLevel(home, resolvedStatus);
|
||||||
|
_logLoadedVipData(home, resolvedStatus, selectedLevel);
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -141,6 +141,11 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
_selectedLevel = level;
|
_selectedLevel = level;
|
||||||
_preview = null;
|
_preview = null;
|
||||||
});
|
});
|
||||||
|
_logSelectedLevelResources(
|
||||||
|
level,
|
||||||
|
config: _levelConfigFor(level),
|
||||||
|
status: _status,
|
||||||
|
);
|
||||||
_loadPreviewForLevel(level);
|
_loadPreviewForLevel(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +201,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
await _loadVipData(showLoading: false);
|
await _loadVipData(showLoading: false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint('VIP purchase failed: $error');
|
|
||||||
} finally {
|
} finally {
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -210,6 +214,56 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
return 'vip-buy-$userId-${DateTime.now().millisecondsSinceEpoch}';
|
return 'vip-buy-$userId-${DateTime.now().millisecondsSinceEpoch}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _logLoadedVipData(
|
||||||
|
SCVipHomeRes home,
|
||||||
|
SCVipStatusRes? status,
|
||||||
|
int selectedLevel,
|
||||||
|
) {
|
||||||
|
if (home.configured != true) {}
|
||||||
|
if (home.levels.isEmpty) {}
|
||||||
|
_logSelectedLevelResources(
|
||||||
|
selectedLevel,
|
||||||
|
config: _levelConfigFromHome(home, selectedLevel),
|
||||||
|
status: status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _logSelectedLevelResources(
|
||||||
|
int level, {
|
||||||
|
required SCVipLevelConfigRes? config,
|
||||||
|
required SCVipStatusRes? status,
|
||||||
|
}) {
|
||||||
|
final state = status?.levelInt == level ? status : null;
|
||||||
|
if (config == null && state == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final badge = _preferResource(config?.badge, state?.badge);
|
||||||
|
final avatarFrame = _preferResource(
|
||||||
|
config?.avatarFrame,
|
||||||
|
state?.avatarFrame,
|
||||||
|
);
|
||||||
|
final entryEffect = _preferResource(
|
||||||
|
config?.entryEffect,
|
||||||
|
state?.entryEffect,
|
||||||
|
);
|
||||||
|
final chatBubble = _preferResource(config?.chatBubble, state?.chatBubble);
|
||||||
|
final floatPicture = _preferResource(
|
||||||
|
config?.floatPicture,
|
||||||
|
state?.floatPicture,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _resourceLogValue(SCVipResourceRes? resource) {
|
||||||
|
if (!_hasResource(resource)) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
return '{id:${resource?.resourceId},name:${resource?.name},'
|
||||||
|
'hasPreviewUrl:${resource?.hasPreviewUrl},'
|
||||||
|
'hasSourceUrl:${resource?.hasSourceUrl},'
|
||||||
|
'previewUrl:${resource?.previewUrl},'
|
||||||
|
'sourceUrl:${resource?.sourceResourceUrl}}';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -470,8 +524,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
|
|
||||||
Widget _buildLevelCard() {
|
Widget _buildLevelCard() {
|
||||||
final assetLevel = _assetLevel;
|
final assetLevel = _assetLevel;
|
||||||
final backgroundCard = _selectedBackgroundCard;
|
|
||||||
final effectImage = _selectedEffectImage ?? _selectedLongBadge;
|
|
||||||
return Center(
|
return Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 351.w,
|
width: 351.w,
|
||||||
@ -480,23 +532,18 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: ClipRRect(
|
child: Container(
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
decoration: BoxDecoration(
|
||||||
child: _buildVipCardBackground(
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
resource: backgroundCard,
|
border: Border.all(
|
||||||
assetLevel: assetLevel,
|
color: const Color(0xFFEBCB76),
|
||||||
),
|
width: 1.w,
|
||||||
),
|
),
|
||||||
),
|
image: DecorationImage(
|
||||||
Positioned.fill(
|
image: AssetImage(
|
||||||
child: IgnorePointer(
|
'sc_images/vip/sc_vip_card_bg_$assetLevel.png',
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFFEBCB76),
|
|
||||||
width: 1.w,
|
|
||||||
),
|
),
|
||||||
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -528,8 +575,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
PositionedDirectional(
|
PositionedDirectional(
|
||||||
end: 24.w,
|
end: 24.w,
|
||||||
top: -36.w,
|
top: -36.w,
|
||||||
child: _buildDynamicResourceVisual(
|
child: _buildResourceImage(
|
||||||
resource: effectImage,
|
resource: _selectedBadge,
|
||||||
width: 135.w,
|
width: 135.w,
|
||||||
height: 135.w,
|
height: 135.w,
|
||||||
fallbackIcon: Icons.workspace_premium_outlined,
|
fallbackIcon: Icons.workspace_premium_outlined,
|
||||||
@ -541,27 +588,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildVipCardBackground({
|
|
||||||
required SCVipResourceRes? resource,
|
|
||||||
required int assetLevel,
|
|
||||||
}) {
|
|
||||||
final fallback = Image.asset(
|
|
||||||
'sc_images/vip/sc_vip_card_bg_$assetLevel.png',
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
);
|
|
||||||
if (!_hasResourceUrl(resource)) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
return SCDataCardResourceView(
|
|
||||||
sourceUrl: resource?.sourceResourceUrl,
|
|
||||||
coverUrl: resource?.previewUrl,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
fallback: fallback,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildLevelDivider() {
|
Widget _buildLevelDivider() {
|
||||||
final ornamentLayout = _ornamentLayoutForLevel(_assetLevel);
|
final ornamentLayout = _ornamentLayoutForLevel(_assetLevel);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@ -687,14 +713,9 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCVipResourceRes? get _selectedLongBadge => _selectedResource(
|
SCVipResourceRes? get _selectedBadge => _selectedResource(
|
||||||
fromConfig: (config) => config.longBadge,
|
fromConfig: (config) => config.badge,
|
||||||
fromState: (status) => status.longBadge,
|
fromState: (status) => status.badge,
|
||||||
);
|
|
||||||
|
|
||||||
SCVipResourceRes? get _selectedShortBadge => _selectedResource(
|
|
||||||
fromConfig: (config) => config.shortBadge,
|
|
||||||
fromState: (status) => status.shortBadge,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
SCVipResourceRes? get _selectedAvatarFrame => _selectedResource(
|
SCVipResourceRes? get _selectedAvatarFrame => _selectedResource(
|
||||||
@ -717,16 +738,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
fromState: (status) => status.floatPicture,
|
fromState: (status) => status.floatPicture,
|
||||||
);
|
);
|
||||||
|
|
||||||
SCVipResourceRes? get _selectedBackgroundCard => _selectedResource(
|
|
||||||
fromConfig: (config) => config.backgroundCard,
|
|
||||||
fromState: (status) => status.backgroundCard,
|
|
||||||
);
|
|
||||||
|
|
||||||
SCVipResourceRes? get _selectedEffectImage => _selectedResource(
|
|
||||||
fromConfig: (config) => config.effectImage,
|
|
||||||
fromState: (status) => status.effectImage,
|
|
||||||
);
|
|
||||||
|
|
||||||
SCVipResourceRes? _selectedResource({
|
SCVipResourceRes? _selectedResource({
|
||||||
required SCVipResourceRes? Function(SCVipLevelConfigRes config) fromConfig,
|
required SCVipResourceRes? Function(SCVipLevelConfigRes config) fromConfig,
|
||||||
required SCVipResourceRes? Function(SCVipStatusRes status) fromState,
|
required SCVipResourceRes? Function(SCVipStatusRes status) fromState,
|
||||||
@ -757,14 +768,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
final items = <_VipFeatureData>[];
|
final items = <_VipFeatureData>[];
|
||||||
_addResourceItem(
|
_addResourceItem(
|
||||||
items,
|
items,
|
||||||
resource: _selectedLongBadge,
|
resource: _selectedBadge,
|
||||||
fallbackTitle: 'Long Badge',
|
fallbackTitle: 'VIP Badge',
|
||||||
fallbackIcon: Icons.workspace_premium_outlined,
|
|
||||||
);
|
|
||||||
_addResourceItem(
|
|
||||||
items,
|
|
||||||
resource: _selectedShortBadge,
|
|
||||||
fallbackTitle: 'Short Badge',
|
|
||||||
fallbackIcon: Icons.workspace_premium_outlined,
|
fallbackIcon: Icons.workspace_premium_outlined,
|
||||||
);
|
);
|
||||||
_addResourceItem(
|
_addResourceItem(
|
||||||
@ -792,19 +797,12 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
fallbackIcon: Icons.airline_seat_recline_extra,
|
fallbackIcon: Icons.airline_seat_recline_extra,
|
||||||
wide: true,
|
wide: true,
|
||||||
);
|
);
|
||||||
_addResourceItem(
|
|
||||||
items,
|
|
||||||
resource: _selectedBackgroundCard,
|
|
||||||
fallbackTitle: 'Profile Card',
|
|
||||||
fallbackIcon: Icons.badge_outlined,
|
|
||||||
wide: true,
|
|
||||||
);
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<_VipFeatureData> get _vipPrivilegeItems {
|
List<_VipFeatureData> get _vipPrivilegeItems {
|
||||||
// The VIP home API currently returns resource perks only:
|
// The VIP home API currently returns resource perks only:
|
||||||
// badges/avatarFrame/entryEffect/chatBubble/floatPicture/backgroundCard.
|
// badge/avatarFrame/entryEffect/chatBubble/floatPicture.
|
||||||
// Do not render demo-only functional privileges without backend data.
|
// Do not render demo-only functional privileges without backend data.
|
||||||
return const <_VipFeatureData>[];
|
return const <_VipFeatureData>[];
|
||||||
}
|
}
|
||||||
@ -846,40 +844,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
return fallbackTitle;
|
return fallbackTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDynamicResourceVisual({
|
|
||||||
required SCVipResourceRes? resource,
|
|
||||||
required double width,
|
|
||||||
required double height,
|
|
||||||
required IconData fallbackIcon,
|
|
||||||
}) {
|
|
||||||
final sourceUrl = resource?.sourceResourceUrl?.trim();
|
|
||||||
final previewUrl = resource?.previewUrl?.trim();
|
|
||||||
final hasSource = sourceUrl != null && sourceUrl.isNotEmpty;
|
|
||||||
final hasPreview = previewUrl != null && previewUrl.isNotEmpty;
|
|
||||||
if (hasSource || hasPreview) {
|
|
||||||
return SCDataCardResourceView(
|
|
||||||
sourceUrl: sourceUrl,
|
|
||||||
coverUrl: previewUrl,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
fallback: _buildResourceImage(
|
|
||||||
resource: resource,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
fallbackIcon: fallbackIcon,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buildResourceImage(
|
|
||||||
resource: resource,
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
fallbackIcon: fallbackIcon,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildResourceImage({
|
Widget _buildResourceImage({
|
||||||
required SCVipResourceRes? resource,
|
required SCVipResourceRes? resource,
|
||||||
required double width,
|
required double width,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
@ -292,20 +291,6 @@ class _PendingRoomStartupSeatAction {
|
|||||||
final String? inviterId;
|
final String? inviterId;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PersistedVoiceRoomCleanupRequest {
|
|
||||||
const _PersistedVoiceRoomCleanupRequest({
|
|
||||||
required this.staleRoomId,
|
|
||||||
required this.targetRoomId,
|
|
||||||
required this.restoreOnlineHeartbeatWhenIdle,
|
|
||||||
this.clearMarkerOnly = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String staleRoomId;
|
|
||||||
final String targetRoomId;
|
|
||||||
final bool restoreOnlineHeartbeatWhenIdle;
|
|
||||||
final bool clearMarkerOnly;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RealTimeCommunicationManager extends ChangeNotifier {
|
class RealTimeCommunicationManager extends ChangeNotifier {
|
||||||
static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog';
|
static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog';
|
||||||
static const String _roomRocketPagLaunchDialogTag =
|
static const String _roomRocketPagLaunchDialogTag =
|
||||||
@ -319,12 +304,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
Duration(seconds: 5),
|
Duration(seconds: 5),
|
||||||
Duration(seconds: 10),
|
Duration(seconds: 10),
|
||||||
];
|
];
|
||||||
static const List<Duration> _roomRocketEntryReadyFallbackDelays = [
|
|
||||||
Duration(milliseconds: 300),
|
|
||||||
Duration(milliseconds: 900),
|
|
||||||
Duration(milliseconds: 1800),
|
|
||||||
Duration(milliseconds: 3200),
|
|
||||||
];
|
|
||||||
static const int _roomPasswordNotTrueErrorCode = 9005;
|
static const int _roomPasswordNotTrueErrorCode = 9005;
|
||||||
|
|
||||||
static const Duration _micListPollingInterval = Duration(seconds: 2);
|
static const Duration _micListPollingInterval = Duration(seconds: 2);
|
||||||
@ -364,10 +343,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
Timer? _onlineUsersPollingTimer;
|
Timer? _onlineUsersPollingTimer;
|
||||||
Timer? _roomEntryEffectTimer;
|
Timer? _roomEntryEffectTimer;
|
||||||
Timer? _roomRocketLaunchAnimationTimer;
|
Timer? _roomRocketLaunchAnimationTimer;
|
||||||
ValueNotifier<String?>? _roomRocketLaunchTopAvatarNotifier;
|
|
||||||
Timer? _roomRocketGiftRefreshTimer;
|
Timer? _roomRocketGiftRefreshTimer;
|
||||||
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
||||||
final List<Timer> _roomRocketEntryReadyFallbackTimers = [];
|
|
||||||
Timer? _roomRedPacketPresenceTimer;
|
Timer? _roomRedPacketPresenceTimer;
|
||||||
Future<void>? _rtcEnginePrewarmTask;
|
Future<void>? _rtcEnginePrewarmTask;
|
||||||
Future<void>? _pendingRoomSwitchRtcLeaveTask;
|
Future<void>? _pendingRoomSwitchRtcLeaveTask;
|
||||||
@ -513,11 +490,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomEntryEffectTimer = null;
|
_roomEntryEffectTimer = null;
|
||||||
_roomRocketLaunchAnimationTimer?.cancel();
|
_roomRocketLaunchAnimationTimer?.cancel();
|
||||||
_roomRocketLaunchAnimationTimer = null;
|
_roomRocketLaunchAnimationTimer = null;
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
|
||||||
_releaseRoomRocketLaunchTopAvatarNotifier();
|
|
||||||
_roomRocketGiftRefreshTimer?.cancel();
|
_roomRocketGiftRefreshTimer?.cancel();
|
||||||
_roomRocketGiftRefreshTimer = null;
|
_roomRocketGiftRefreshTimer = null;
|
||||||
_cancelRoomRocketEntryReadyFallbacks();
|
|
||||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||||
}
|
}
|
||||||
_roomVisualEffectsEnabled = enabled;
|
_roomVisualEffectsEnabled = enabled;
|
||||||
@ -526,12 +500,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void setVoiceRoomRouteVisible(bool visible) {
|
void setVoiceRoomRouteVisible(bool visible) {
|
||||||
_voiceRoomRouteVisible = visible;
|
_voiceRoomRouteVisible = visible;
|
||||||
if (visible) {
|
|
||||||
final roomId = (currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
|
||||||
if (roomId.isNotEmpty) {
|
|
||||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setRoomStartupLoading() {
|
void _setRoomStartupLoading() {
|
||||||
@ -1812,11 +1780,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final persistedRoomCleanupRequest =
|
await cleanupPersistedVoiceRoomSessionBeforeEntry(targetRoomId: roomId);
|
||||||
_preparePersistedVoiceRoomCleanupBeforeEntry(
|
if (!context.mounted) {
|
||||||
targetRoomId: roomId,
|
return;
|
||||||
restoreOnlineHeartbeatWhenIdle: false,
|
}
|
||||||
);
|
|
||||||
if (roomId == currenRoom?.roomProfile?.roomProfile?.id) {
|
if (roomId == currenRoom?.roomProfile?.roomProfile?.id) {
|
||||||
if (_currentRoomIsEntryPreview ||
|
if (_currentRoomIsEntryPreview ||
|
||||||
_roomStartupStatus == RoomStartupStatus.loading) {
|
_roomStartupStatus == RoomStartupStatus.loading) {
|
||||||
@ -1825,9 +1792,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
_startVoiceRoomForegroundService();
|
_startVoiceRoomForegroundService();
|
||||||
VoiceRoomRoute.openVoiceRoom(context);
|
VoiceRoomRoute.openVoiceRoom(context);
|
||||||
_startPersistedVoiceRoomCleanupInBackground(
|
|
||||||
persistedRoomCleanupRequest,
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1866,7 +1830,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_refreshRoomRedPacketListAfterEntry(roomId: roomId);
|
_refreshRoomRedPacketListAfterEntry(roomId: roomId);
|
||||||
_startVoiceRoomForegroundService();
|
_startVoiceRoomForegroundService();
|
||||||
VoiceRoomRoute.openVoiceRoom(context);
|
VoiceRoomRoute.openVoiceRoom(context);
|
||||||
_startPersistedVoiceRoomCleanupInBackground(persistedRoomCleanupRequest);
|
|
||||||
if (shouldRejoinRoomRtc) {
|
if (shouldRejoinRoomRtc) {
|
||||||
unawaited(_rejoinCurrentRoomRtcForSameRoomReuse(roomId));
|
unawaited(_rejoinCurrentRoomRtcForSameRoomReuse(roomId));
|
||||||
}
|
}
|
||||||
@ -1902,7 +1865,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
_startVoiceRoomForegroundService();
|
_startVoiceRoomForegroundService();
|
||||||
VoiceRoomRoute.openVoiceRoom(context);
|
VoiceRoomRoute.openVoiceRoom(context);
|
||||||
_startPersistedVoiceRoomCleanupInBackground(persistedRoomCleanupRequest);
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
final entryRequestSerial = ++_roomEntryRequestSerial;
|
final entryRequestSerial = ++_roomEntryRequestSerial;
|
||||||
if (preEnteredRoom != null) {
|
if (preEnteredRoom != null) {
|
||||||
@ -2525,70 +2487,14 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
Future<void> cleanupPersistedVoiceRoomSessionBeforeEntry({
|
Future<void> cleanupPersistedVoiceRoomSessionBeforeEntry({
|
||||||
String? targetRoomId,
|
String? targetRoomId,
|
||||||
bool restoreOnlineHeartbeatWhenIdle = true,
|
|
||||||
}) {
|
}) {
|
||||||
final cleanupRequest = _preparePersistedVoiceRoomCleanupBeforeEntry(
|
|
||||||
targetRoomId: targetRoomId,
|
|
||||||
restoreOnlineHeartbeatWhenIdle: restoreOnlineHeartbeatWhenIdle,
|
|
||||||
);
|
|
||||||
return _runPersistedVoiceRoomCleanup(cleanupRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
_PersistedVoiceRoomCleanupRequest?
|
|
||||||
_preparePersistedVoiceRoomCleanupBeforeEntry({
|
|
||||||
String? targetRoomId,
|
|
||||||
bool restoreOnlineHeartbeatWhenIdle = true,
|
|
||||||
}) {
|
|
||||||
final staleRoomId = DataPersistence.getLastTimeRoomId().trim();
|
|
||||||
if (staleRoomId.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentRoomId =
|
|
||||||
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
|
||||||
if (currentRoomId.isNotEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentUserId =
|
|
||||||
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
|
|
||||||
if (currentUserId.isEmpty || AccountStorage().getToken().trim().isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final normalizedTargetRoomId = (targetRoomId ?? "").trim();
|
|
||||||
if (normalizedTargetRoomId.isNotEmpty &&
|
|
||||||
normalizedTargetRoomId == staleRoomId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final staleRoomUserId = DataPersistence.getLastTimeRoomUserId().trim();
|
|
||||||
return _PersistedVoiceRoomCleanupRequest(
|
|
||||||
staleRoomId: staleRoomId,
|
|
||||||
targetRoomId: normalizedTargetRoomId,
|
|
||||||
restoreOnlineHeartbeatWhenIdle: restoreOnlineHeartbeatWhenIdle,
|
|
||||||
clearMarkerOnly:
|
|
||||||
staleRoomUserId.isNotEmpty && staleRoomUserId != currentUserId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startPersistedVoiceRoomCleanupInBackground(
|
|
||||||
_PersistedVoiceRoomCleanupRequest? cleanupRequest,
|
|
||||||
) {
|
|
||||||
unawaited(_runPersistedVoiceRoomCleanup(cleanupRequest));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _runPersistedVoiceRoomCleanup(
|
|
||||||
_PersistedVoiceRoomCleanupRequest? cleanupRequest,
|
|
||||||
) {
|
|
||||||
if (cleanupRequest == null) {
|
|
||||||
return Future<void>.value();
|
|
||||||
}
|
|
||||||
final runningTask = _persistedRoomCleanupTask;
|
final runningTask = _persistedRoomCleanupTask;
|
||||||
if (runningTask != null) {
|
if (runningTask != null) {
|
||||||
return runningTask;
|
return runningTask;
|
||||||
}
|
}
|
||||||
final task = _cleanupPersistedVoiceRoomSessionBeforeEntry(cleanupRequest);
|
final task = _cleanupPersistedVoiceRoomSessionBeforeEntry(
|
||||||
|
targetRoomId: targetRoomId,
|
||||||
|
);
|
||||||
_persistedRoomCleanupTask = task;
|
_persistedRoomCleanupTask = task;
|
||||||
return task.whenComplete(() {
|
return task.whenComplete(() {
|
||||||
if (identical(_persistedRoomCleanupTask, task)) {
|
if (identical(_persistedRoomCleanupTask, task)) {
|
||||||
@ -2597,11 +2503,28 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _cleanupPersistedVoiceRoomSessionBeforeEntry(
|
Future<void> _cleanupPersistedVoiceRoomSessionBeforeEntry({
|
||||||
_PersistedVoiceRoomCleanupRequest cleanupRequest,
|
String? targetRoomId,
|
||||||
) async {
|
}) async {
|
||||||
final staleRoomId = cleanupRequest.staleRoomId;
|
final staleRoomId = DataPersistence.getLastTimeRoomId().trim();
|
||||||
if (cleanupRequest.clearMarkerOnly) {
|
if (staleRoomId.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentRoomId =
|
||||||
|
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
|
if (currentRoomId.isNotEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final currentUserId =
|
||||||
|
(AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim();
|
||||||
|
if (currentUserId.isEmpty || AccountStorage().getToken().trim().isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final staleRoomUserId = DataPersistence.getLastTimeRoomUserId().trim();
|
||||||
|
if (staleRoomUserId.isNotEmpty && staleRoomUserId != currentUserId) {
|
||||||
await _clearPersistedRoomMarkerIfMatches(staleRoomId);
|
await _clearPersistedRoomMarkerIfMatches(staleRoomId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2622,11 +2545,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
if (didClean) {
|
if (didClean) {
|
||||||
await _clearPersistedRoomMarkerIfMatches(staleRoomId);
|
await _clearPersistedRoomMarkerIfMatches(staleRoomId);
|
||||||
final currentRoomId =
|
if ((targetRoomId ?? "").trim() != staleRoomId) {
|
||||||
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
|
||||||
if (cleanupRequest.restoreOnlineHeartbeatWhenIdle &&
|
|
||||||
cleanupRequest.targetRoomId != staleRoomId &&
|
|
||||||
currentRoomId.isEmpty) {
|
|
||||||
await SCHeartbeatUtils.scheduleHeartbeat(
|
await SCHeartbeatUtils.scheduleHeartbeat(
|
||||||
SCHeartbeatStatus.ONLINE.name,
|
SCHeartbeatStatus.ONLINE.name,
|
||||||
false,
|
false,
|
||||||
@ -2675,7 +2594,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
setRoomVisualEffectsEnabled(true);
|
setRoomVisualEffectsEnabled(true);
|
||||||
if (shouldOpenRoomPage) {
|
if (shouldOpenRoomPage) {
|
||||||
VoiceRoomRoute.openVoiceRoom(context!);
|
VoiceRoomRoute.openVoiceRoom(context!);
|
||||||
setVoiceRoomRouteVisible(true);
|
|
||||||
}
|
}
|
||||||
unawaited(
|
unawaited(
|
||||||
_bootstrapEnteredVoiceRoomSession(
|
_bootstrapEnteredVoiceRoomSession(
|
||||||
@ -2776,7 +2694,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
);
|
);
|
||||||
_loadRoomSecondaryData();
|
_loadRoomSecondaryData();
|
||||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
if (!_isActiveRoomStartup(entryRequestSerial, roomId)) {
|
if (!_isActiveRoomStartup(entryRequestSerial, roomId)) {
|
||||||
return;
|
return;
|
||||||
@ -2889,19 +2806,18 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void _loadRoomSecondaryData() {
|
void _loadRoomSecondaryData() {
|
||||||
final roomId = currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
final roomId = currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||||
|
SCChatRoomRepository().rocketClaim(roomId).catchError((e) {
|
||||||
|
return true; // 错误已处理
|
||||||
|
});
|
||||||
SCChatRoomRepository()
|
SCChatRoomRepository()
|
||||||
.rocketStatus(roomId)
|
.rocketStatus(roomId)
|
||||||
.then((res) {
|
.then((res) {
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (!res.isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_scheduleRoomRocketAssetPreload(roomId, res, afterRoomEntry: true);
|
_scheduleRoomRocketAssetPreload(roomId, res, afterRoomEntry: true);
|
||||||
_scheduleRoomRocketEntryReadyFallback(roomId);
|
|
||||||
_loadRoomRocketRewardPopups(roomId);
|
|
||||||
})
|
})
|
||||||
.catchError((e) {});
|
.catchError((e) {});
|
||||||
|
_loadRoomRocketRewardPopups(roomId);
|
||||||
_refreshRoomRedPacketListAfterEntry();
|
_refreshRoomRedPacketListAfterEntry();
|
||||||
|
|
||||||
fetchRoomTaskClaimableCount();
|
fetchRoomTaskClaimableCount();
|
||||||
@ -2957,9 +2873,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
void updateRoomRocketConfigurationStatus(SCRoomRocketStatusRes res) {
|
void updateRoomRocketConfigurationStatus(SCRoomRocketStatusRes res) {
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (!res.isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_scheduleRoomRocketAssetPreload(
|
_scheduleRoomRocketAssetPreload(
|
||||||
(res.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(),
|
(res.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(),
|
||||||
res,
|
res,
|
||||||
@ -2980,9 +2893,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (!res.isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_scheduleRoomRocketAssetPreload(resolvedRoomId, res);
|
_scheduleRoomRocketAssetPreload(resolvedRoomId, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2995,9 +2905,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (resolvedRoomId.isEmpty) {
|
if (resolvedRoomId.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!status.isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (afterRoomEntry) {
|
if (afterRoomEntry) {
|
||||||
RoomRocketAssetPreloader.preloadAfterRoomEntry(
|
RoomRocketAssetPreloader.preloadAfterRoomEntry(
|
||||||
roomId: resolvedRoomId,
|
roomId: resolvedRoomId,
|
||||||
@ -3030,42 +2937,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _scheduleRoomRocketEntryReadyFallback(String roomId) {
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
if (normalizedRoomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
|
|
||||||
for (final delay in _roomRocketEntryReadyFallbackDelays) {
|
|
||||||
final timer = Timer(delay, () {
|
|
||||||
_roomRocketEntryReadyFallbackTimers.removeWhere(
|
|
||||||
(item) => !item.isActive,
|
|
||||||
);
|
|
||||||
_notifyRoomRocketEntryReadyIfCurrent(normalizedRoomId);
|
|
||||||
});
|
|
||||||
_roomRocketEntryReadyFallbackTimers.add(timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notifyRoomRocketEntryReadyIfCurrent(String roomId) {
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
final currentRoomId =
|
|
||||||
currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? "";
|
|
||||||
if (normalizedRoomId.isEmpty ||
|
|
||||||
currentRoomId != normalizedRoomId ||
|
|
||||||
!_voiceRoomRouteVisible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rtmProvider?.handleVoiceRoomReadyForRocketRewards(normalizedRoomId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _cancelRoomRocketEntryReadyFallbacks() {
|
|
||||||
for (final timer in _roomRocketEntryReadyFallbackTimers) {
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
_roomRocketEntryReadyFallbackTimers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduleRoomRocketPostLaunchStatusRefresh({String? roomId}) {
|
void scheduleRoomRocketPostLaunchStatusRefresh({String? roomId}) {
|
||||||
final resolvedRoomId =
|
final resolvedRoomId =
|
||||||
(roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
(roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
||||||
@ -3102,7 +2973,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_roomRocketLaunchAnimationTimer?.cancel();
|
_roomRocketLaunchAnimationTimer?.cancel();
|
||||||
_releaseRoomRocketLaunchTopAvatarNotifier();
|
|
||||||
|
|
||||||
if (shouldShowRoomVisualEffects && isVoiceRoomRouteVisible) {
|
if (shouldShowRoomVisualEffects && isVoiceRoomRouteVisible) {
|
||||||
final animationUrl =
|
final animationUrl =
|
||||||
@ -3120,7 +2990,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomRocketLaunchAnimationTimer = Timer(launch.displayDuration, () {
|
_roomRocketLaunchAnimationTimer = Timer(launch.displayDuration, () {
|
||||||
_roomRocketLaunchAnimationTimer = null;
|
_roomRocketLaunchAnimationTimer = null;
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
||||||
_releaseRoomRocketLaunchTopAvatarNotifier();
|
|
||||||
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
||||||
scheduleRoomRocketPostLaunchStatusRefresh(roomId: launch.roomId);
|
scheduleRoomRocketPostLaunchStatusRefresh(roomId: launch.roomId);
|
||||||
});
|
});
|
||||||
@ -3146,11 +3015,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
required RoomRocketLaunchBroadcastMessage launch,
|
required RoomRocketLaunchBroadcastMessage launch,
|
||||||
}) {
|
}) {
|
||||||
if (RoomRocketPagEffectOverlay.isPag(animationUrl)) {
|
if (RoomRocketPagEffectOverlay.isPag(animationUrl)) {
|
||||||
final topAvatarNotifier = ValueNotifier<String?>(
|
|
||||||
_roomRocketTop1AvatarUrl(launch.safeLevel),
|
|
||||||
);
|
|
||||||
_roomRocketLaunchTopAvatarNotifier = topAvatarNotifier;
|
|
||||||
unawaited(_loadRoomRocketTop1Avatar(launch, topAvatarNotifier));
|
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
||||||
SmartDialog.show(
|
SmartDialog.show(
|
||||||
tag: _roomRocketPagLaunchDialogTag,
|
tag: _roomRocketPagLaunchDialogTag,
|
||||||
@ -3162,12 +3026,11 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
builder:
|
builder:
|
||||||
(_) => RoomRocketPagEffectOverlay(
|
(_) => RoomRocketPagEffectOverlay(
|
||||||
resource: animationUrl,
|
resource: animationUrl,
|
||||||
topAvatarUrlListenable: topAvatarNotifier,
|
topAvatarUrl: _roomRocketTop1AvatarUrl(),
|
||||||
onCompleted: () {
|
onCompleted: () {
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
||||||
_roomRocketLaunchAnimationTimer?.cancel();
|
_roomRocketLaunchAnimationTimer?.cancel();
|
||||||
_roomRocketLaunchAnimationTimer = null;
|
_roomRocketLaunchAnimationTimer = null;
|
||||||
_releaseRoomRocketLaunchTopAvatarNotifier(topAvatarNotifier);
|
|
||||||
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
unawaited(refreshRoomRocketStatus(roomId: launch.roomId));
|
||||||
scheduleRoomRocketPostLaunchStatusRefresh(
|
scheduleRoomRocketPostLaunchStatusRefresh(
|
||||||
roomId: launch.roomId,
|
roomId: launch.roomId,
|
||||||
@ -3184,21 +3047,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _roomRocketTop1AvatarUrl(int level) {
|
String? _roomRocketTop1AvatarUrl() {
|
||||||
final kings =
|
final kings = roomRocketStatus?.rocketKings ?? const [];
|
||||||
roomRocketStatus?.rocketKingsForLevel(level) ??
|
|
||||||
const <SCRoomRocketKingUserRes>[];
|
|
||||||
return _roomRocketTop1AvatarUrlFromUsers(kings);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _roomRocketTop1AvatarUrlFromKing(SCRoomRocketKingRes king) {
|
|
||||||
final source = king.podium.isNotEmpty ? king.podium : king.displayRecords;
|
|
||||||
return _roomRocketTop1AvatarUrlFromUsers(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _roomRocketTop1AvatarUrlFromUsers(
|
|
||||||
List<SCRoomRocketKingUserRes> kings,
|
|
||||||
) {
|
|
||||||
if (kings.isEmpty) {
|
if (kings.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -3215,90 +3065,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadRoomRocketTop1Avatar(
|
|
||||||
RoomRocketLaunchBroadcastMessage launch,
|
|
||||||
ValueNotifier<String?> notifier,
|
|
||||||
) async {
|
|
||||||
final roomId = launch.roomId.trim();
|
|
||||||
if (roomId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
_roomRocketDebug(
|
|
||||||
'request launch top1 avatar roomId=$roomId '
|
|
||||||
'level=${launch.safeLevel} roundNo=${launch.roundNo}',
|
|
||||||
);
|
|
||||||
var king = await SCChatRoomRepository().roomRocketKing(
|
|
||||||
roomId,
|
|
||||||
level: launch.safeLevel,
|
|
||||||
roundNo: launch.roundNo,
|
|
||||||
cursor: 1,
|
|
||||||
limit: 3,
|
|
||||||
);
|
|
||||||
var avatar = _roomRocketTop1AvatarUrlFromKing(king);
|
|
||||||
if (avatar == null && launch.roundNo > 0) {
|
|
||||||
_roomRocketDebug(
|
|
||||||
'launch top1 avatar retry without roundNo '
|
|
||||||
'level=${launch.safeLevel} roundNo=${launch.roundNo}',
|
|
||||||
);
|
|
||||||
king = await SCChatRoomRepository().roomRocketKing(
|
|
||||||
roomId,
|
|
||||||
level: launch.safeLevel,
|
|
||||||
cursor: 1,
|
|
||||||
limit: 3,
|
|
||||||
);
|
|
||||||
avatar = _roomRocketTop1AvatarUrlFromKing(king);
|
|
||||||
}
|
|
||||||
if (_roomRocketLaunchTopAvatarNotifier != notifier) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomRocketDebug(
|
|
||||||
'launch top1 avatar loaded level=${launch.safeLevel} '
|
|
||||||
'roundNo=${launch.roundNo} avatar=${_shortRoomRocketDebugUrl(avatar)} '
|
|
||||||
'podium=${king.podium.length} records=${king.records.length}',
|
|
||||||
);
|
|
||||||
if (avatar != null && avatar != notifier.value) {
|
|
||||||
notifier.value = avatar;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
_roomRocketDebug(
|
|
||||||
'launch top1 avatar load failed level=${launch.safeLevel} '
|
|
||||||
'roundNo=${launch.roundNo}: $error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _releaseRoomRocketLaunchTopAvatarNotifier([
|
|
||||||
ValueNotifier<String?>? expected,
|
|
||||||
]) {
|
|
||||||
final notifier = _roomRocketLaunchTopAvatarNotifier;
|
|
||||||
if (notifier == null || (expected != null && notifier != expected)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomRocketLaunchTopAvatarNotifier = null;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
notifier.dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _roomRocketDebug(String message) {
|
|
||||||
if (!kDebugMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugPrint('[RoomRocket] $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
String _shortRoomRocketDebugUrl(String? url) {
|
|
||||||
final value = url?.trim() ?? '';
|
|
||||||
if (value.isEmpty) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
if (value.length <= 96) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return '${value.substring(0, 48)}...${value.substring(value.length - 24)}';
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomRocketRewardUserProfile? roomRocketRewardUserProfileForRecord(
|
RoomRocketRewardUserProfile? roomRocketRewardUserProfileForRecord(
|
||||||
SCRoomRocketRewardRecordRes record,
|
SCRoomRocketRewardRecordRes record,
|
||||||
) {
|
) {
|
||||||
@ -3601,6 +3367,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
final exitRtmProvider = rtmProvider;
|
final exitRtmProvider = rtmProvider;
|
||||||
final navigationContext = context;
|
final navigationContext = context;
|
||||||
try {
|
try {
|
||||||
|
exitRtmProvider?.msgAllListener = null;
|
||||||
|
exitRtmProvider?.msgChatListener = null;
|
||||||
|
exitRtmProvider?.msgGiftListener = null;
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
SCGiftVapSvgaManager().stopPlayback();
|
||||||
setRoomVisualEffectsEnabled(false);
|
setRoomVisualEffectsEnabled(false);
|
||||||
SCHeartbeatUtils.cancelTimer();
|
SCHeartbeatUtils.cancelTimer();
|
||||||
@ -3636,6 +3405,9 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
final isPreviewOnlyExit = _currentRoomIsEntryPreview;
|
final isPreviewOnlyExit = _currentRoomIsEntryPreview;
|
||||||
final exitRtmProvider = rtmProvider;
|
final exitRtmProvider = rtmProvider;
|
||||||
try {
|
try {
|
||||||
|
exitRtmProvider?.msgAllListener = null;
|
||||||
|
exitRtmProvider?.msgChatListener = null;
|
||||||
|
exitRtmProvider?.msgGiftListener = null;
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
SCGiftVapSvgaManager().stopPlayback();
|
||||||
setRoomVisualEffectsEnabled(false);
|
setRoomVisualEffectsEnabled(false);
|
||||||
SCHeartbeatUtils.cancelTimer();
|
SCHeartbeatUtils.cancelTimer();
|
||||||
@ -3841,8 +3613,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomEntryEffectTimer = null;
|
_roomEntryEffectTimer = null;
|
||||||
_roomRocketLaunchAnimationTimer?.cancel();
|
_roomRocketLaunchAnimationTimer?.cancel();
|
||||||
_roomRocketLaunchAnimationTimer = null;
|
_roomRocketLaunchAnimationTimer = null;
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
|
||||||
_releaseRoomRocketLaunchTopAvatarNotifier();
|
|
||||||
_roomRocketGiftRefreshTimer?.cancel();
|
_roomRocketGiftRefreshTimer?.cancel();
|
||||||
_roomRocketGiftRefreshTimer = null;
|
_roomRocketGiftRefreshTimer = null;
|
||||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -37,8 +37,6 @@ class SCRoomRocketStatusRes {
|
|||||||
bool? hasClaimed,
|
bool? hasClaimed,
|
||||||
num? myContribution,
|
num? myContribution,
|
||||||
num? myContributionRate,
|
num? myContributionRate,
|
||||||
bool? configured,
|
|
||||||
bool? enabled,
|
|
||||||
int? roundNo,
|
int? roundNo,
|
||||||
bool? displayRound,
|
bool? displayRound,
|
||||||
int? currentLevel,
|
int? currentLevel,
|
||||||
@ -71,8 +69,6 @@ class SCRoomRocketStatusRes {
|
|||||||
_hasClaimed = hasClaimed;
|
_hasClaimed = hasClaimed;
|
||||||
_myContribution = myContribution;
|
_myContribution = myContribution;
|
||||||
_myContributionRate = myContributionRate;
|
_myContributionRate = myContributionRate;
|
||||||
_configured = configured;
|
|
||||||
_enabled = enabled;
|
|
||||||
_roundNo = roundNo;
|
_roundNo = roundNo;
|
||||||
_displayRound = displayRound;
|
_displayRound = displayRound;
|
||||||
_currentLevel = currentLevel;
|
_currentLevel = currentLevel;
|
||||||
@ -91,15 +87,6 @@ class SCRoomRocketStatusRes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SCRoomRocketStatusRes.fromJson(dynamic json) {
|
SCRoomRocketStatusRes.fromJson(dynamic json) {
|
||||||
_configured = _boolValue(json['configured']);
|
|
||||||
_enabled =
|
|
||||||
_boolValue(
|
|
||||||
json['enabled'] ??
|
|
||||||
json['enable'] ??
|
|
||||||
json['rocketEnabled'] ??
|
|
||||||
json['rocket_enabled'],
|
|
||||||
) ??
|
|
||||||
true;
|
|
||||||
_roomId = json['roomId'] ?? json['room_id'];
|
_roomId = json['roomId'] ?? json['room_id'];
|
||||||
_date = json['date'] ?? json['dayKey'] ?? json['day_key'];
|
_date = json['date'] ?? json['dayKey'] ?? json['day_key'];
|
||||||
_roundNo = _intValue(json['roundNo'] ?? json['round_no']);
|
_roundNo = _intValue(json['roundNo'] ?? json['round_no']);
|
||||||
@ -232,8 +219,6 @@ class SCRoomRocketStatusRes {
|
|||||||
bool? _hasClaimed;
|
bool? _hasClaimed;
|
||||||
num? _myContribution;
|
num? _myContribution;
|
||||||
num? _myContributionRate;
|
num? _myContributionRate;
|
||||||
bool? _configured;
|
|
||||||
bool? _enabled;
|
|
||||||
int? _roundNo;
|
int? _roundNo;
|
||||||
bool? _displayRound;
|
bool? _displayRound;
|
||||||
int? _currentLevel;
|
int? _currentLevel;
|
||||||
@ -282,17 +267,6 @@ class SCRoomRocketStatusRes {
|
|||||||
|
|
||||||
num? get myContributionRate => _myContributionRate;
|
num? get myContributionRate => _myContributionRate;
|
||||||
|
|
||||||
bool? get configured => _configured;
|
|
||||||
|
|
||||||
bool? get enabled => _enabled;
|
|
||||||
|
|
||||||
bool get isEnabled {
|
|
||||||
final normalizedStatus = (_status ?? '').trim().toUpperCase();
|
|
||||||
return _configured != false &&
|
|
||||||
_enabled != false &&
|
|
||||||
normalizedStatus != 'DISABLED';
|
|
||||||
}
|
|
||||||
|
|
||||||
int? get roundNo => _roundNo;
|
int? get roundNo => _roundNo;
|
||||||
|
|
||||||
bool? get displayRound => _displayRound;
|
bool? get displayRound => _displayRound;
|
||||||
@ -397,8 +371,6 @@ class SCRoomRocketStatusRes {
|
|||||||
map['hasClaimed'] = _hasClaimed;
|
map['hasClaimed'] = _hasClaimed;
|
||||||
map['myContribution'] = _myContribution;
|
map['myContribution'] = _myContribution;
|
||||||
map['myContributionRate'] = _myContributionRate;
|
map['myContributionRate'] = _myContributionRate;
|
||||||
map['configured'] = _configured;
|
|
||||||
map['enabled'] = _enabled;
|
|
||||||
map['roundNo'] = _roundNo;
|
map['roundNo'] = _roundNo;
|
||||||
map['displayRound'] = _displayRound;
|
map['displayRound'] = _displayRound;
|
||||||
map['currentLevel'] = _currentLevel;
|
map['currentLevel'] = _currentLevel;
|
||||||
|
|||||||
@ -110,14 +110,10 @@ class SCVipStatusRes {
|
|||||||
String? startAt,
|
String? startAt,
|
||||||
String? expireAt,
|
String? expireAt,
|
||||||
SCVipResourceRes? badge,
|
SCVipResourceRes? badge,
|
||||||
SCVipResourceRes? longBadge,
|
|
||||||
SCVipResourceRes? shortBadge,
|
|
||||||
SCVipResourceRes? avatarFrame,
|
SCVipResourceRes? avatarFrame,
|
||||||
SCVipResourceRes? entryEffect,
|
SCVipResourceRes? entryEffect,
|
||||||
SCVipResourceRes? chatBubble,
|
SCVipResourceRes? chatBubble,
|
||||||
SCVipResourceRes? floatPicture,
|
SCVipResourceRes? floatPicture,
|
||||||
SCVipResourceRes? backgroundCard,
|
|
||||||
SCVipResourceRes? effectImage,
|
|
||||||
}) {
|
}) {
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
_sysOrigin = sysOrigin;
|
_sysOrigin = sysOrigin;
|
||||||
@ -129,14 +125,10 @@ class SCVipStatusRes {
|
|||||||
_startAt = startAt;
|
_startAt = startAt;
|
||||||
_expireAt = expireAt;
|
_expireAt = expireAt;
|
||||||
_badge = badge;
|
_badge = badge;
|
||||||
_longBadge = longBadge;
|
|
||||||
_shortBadge = shortBadge;
|
|
||||||
_avatarFrame = avatarFrame;
|
_avatarFrame = avatarFrame;
|
||||||
_entryEffect = entryEffect;
|
_entryEffect = entryEffect;
|
||||||
_chatBubble = chatBubble;
|
_chatBubble = chatBubble;
|
||||||
_floatPicture = floatPicture;
|
_floatPicture = floatPicture;
|
||||||
_backgroundCard = backgroundCard;
|
|
||||||
_effectImage = effectImage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SCVipStatusRes.fromJson(dynamic json) {
|
SCVipStatusRes.fromJson(dynamic json) {
|
||||||
@ -152,14 +144,10 @@ class SCVipStatusRes {
|
|||||||
_startAt = _stringValue(map['startAt']);
|
_startAt = _stringValue(map['startAt']);
|
||||||
_expireAt = _stringValue(map['expireAt']);
|
_expireAt = _stringValue(map['expireAt']);
|
||||||
_badge = _resourceValue(map['badge']);
|
_badge = _resourceValue(map['badge']);
|
||||||
_longBadge = _resourceValue(map['longBadge']) ?? _badge;
|
|
||||||
_shortBadge = _resourceValue(map['shortBadge']);
|
|
||||||
_avatarFrame = _resourceValue(map['avatarFrame']);
|
_avatarFrame = _resourceValue(map['avatarFrame']);
|
||||||
_entryEffect = _resourceValue(map['entryEffect']);
|
_entryEffect = _resourceValue(map['entryEffect']);
|
||||||
_chatBubble = _resourceValue(map['chatBubble']);
|
_chatBubble = _resourceValue(map['chatBubble']);
|
||||||
_floatPicture = _resourceValue(map['floatPicture']);
|
_floatPicture = _resourceValue(map['floatPicture']);
|
||||||
_backgroundCard = _resourceValue(map['backgroundCard']);
|
|
||||||
_effectImage = _resourceValue(map['effectImage']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _userId;
|
String? _userId;
|
||||||
@ -172,14 +160,10 @@ class SCVipStatusRes {
|
|||||||
String? _startAt;
|
String? _startAt;
|
||||||
String? _expireAt;
|
String? _expireAt;
|
||||||
SCVipResourceRes? _badge;
|
SCVipResourceRes? _badge;
|
||||||
SCVipResourceRes? _longBadge;
|
|
||||||
SCVipResourceRes? _shortBadge;
|
|
||||||
SCVipResourceRes? _avatarFrame;
|
SCVipResourceRes? _avatarFrame;
|
||||||
SCVipResourceRes? _entryEffect;
|
SCVipResourceRes? _entryEffect;
|
||||||
SCVipResourceRes? _chatBubble;
|
SCVipResourceRes? _chatBubble;
|
||||||
SCVipResourceRes? _floatPicture;
|
SCVipResourceRes? _floatPicture;
|
||||||
SCVipResourceRes? _backgroundCard;
|
|
||||||
SCVipResourceRes? _effectImage;
|
|
||||||
|
|
||||||
String? get userId => _userId;
|
String? get userId => _userId;
|
||||||
|
|
||||||
@ -199,11 +183,7 @@ class SCVipStatusRes {
|
|||||||
|
|
||||||
String? get expireAt => _expireAt;
|
String? get expireAt => _expireAt;
|
||||||
|
|
||||||
SCVipResourceRes? get badge => longBadge;
|
SCVipResourceRes? get badge => _badge;
|
||||||
|
|
||||||
SCVipResourceRes? get longBadge => _longBadge ?? _badge;
|
|
||||||
|
|
||||||
SCVipResourceRes? get shortBadge => _shortBadge;
|
|
||||||
|
|
||||||
SCVipResourceRes? get avatarFrame => _avatarFrame;
|
SCVipResourceRes? get avatarFrame => _avatarFrame;
|
||||||
|
|
||||||
@ -213,10 +193,6 @@ class SCVipStatusRes {
|
|||||||
|
|
||||||
SCVipResourceRes? get floatPicture => _floatPicture;
|
SCVipResourceRes? get floatPicture => _floatPicture;
|
||||||
|
|
||||||
SCVipResourceRes? get backgroundCard => _backgroundCard;
|
|
||||||
|
|
||||||
SCVipResourceRes? get effectImage => _effectImage;
|
|
||||||
|
|
||||||
bool get isActive => _active == true && (_level ?? 0) > 0;
|
bool get isActive => _active == true && (_level ?? 0) > 0;
|
||||||
|
|
||||||
int get levelInt => (_level ?? 0).toInt();
|
int get levelInt => (_level ?? 0).toInt();
|
||||||
@ -240,17 +216,11 @@ class SCVipStatusRes {
|
|||||||
map['displayName'] = _displayName;
|
map['displayName'] = _displayName;
|
||||||
map['startAt'] = _startAt;
|
map['startAt'] = _startAt;
|
||||||
map['expireAt'] = _expireAt;
|
map['expireAt'] = _expireAt;
|
||||||
if (badge != null) map['badge'] = badge?.toJson();
|
if (_badge != null) map['badge'] = _badge?.toJson();
|
||||||
if (longBadge != null) map['longBadge'] = longBadge?.toJson();
|
|
||||||
if (_shortBadge != null) map['shortBadge'] = _shortBadge?.toJson();
|
|
||||||
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
|
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
|
||||||
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
|
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
|
||||||
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
|
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
|
||||||
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
|
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
|
||||||
if (_backgroundCard != null) {
|
|
||||||
map['backgroundCard'] = _backgroundCard?.toJson();
|
|
||||||
}
|
|
||||||
if (_effectImage != null) map['effectImage'] = _effectImage?.toJson();
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,14 +234,10 @@ class SCVipLevelConfigRes {
|
|||||||
num? durationDays,
|
num? durationDays,
|
||||||
num? priceGold,
|
num? priceGold,
|
||||||
SCVipResourceRes? badge,
|
SCVipResourceRes? badge,
|
||||||
SCVipResourceRes? longBadge,
|
|
||||||
SCVipResourceRes? shortBadge,
|
|
||||||
SCVipResourceRes? avatarFrame,
|
SCVipResourceRes? avatarFrame,
|
||||||
SCVipResourceRes? entryEffect,
|
SCVipResourceRes? entryEffect,
|
||||||
SCVipResourceRes? chatBubble,
|
SCVipResourceRes? chatBubble,
|
||||||
SCVipResourceRes? floatPicture,
|
SCVipResourceRes? floatPicture,
|
||||||
SCVipResourceRes? backgroundCard,
|
|
||||||
SCVipResourceRes? effectImage,
|
|
||||||
bool? canPurchase,
|
bool? canPurchase,
|
||||||
num? payableGold,
|
num? payableGold,
|
||||||
}) {
|
}) {
|
||||||
@ -282,14 +248,10 @@ class SCVipLevelConfigRes {
|
|||||||
_durationDays = durationDays;
|
_durationDays = durationDays;
|
||||||
_priceGold = priceGold;
|
_priceGold = priceGold;
|
||||||
_badge = badge;
|
_badge = badge;
|
||||||
_longBadge = longBadge;
|
|
||||||
_shortBadge = shortBadge;
|
|
||||||
_avatarFrame = avatarFrame;
|
_avatarFrame = avatarFrame;
|
||||||
_entryEffect = entryEffect;
|
_entryEffect = entryEffect;
|
||||||
_chatBubble = chatBubble;
|
_chatBubble = chatBubble;
|
||||||
_floatPicture = floatPicture;
|
_floatPicture = floatPicture;
|
||||||
_backgroundCard = backgroundCard;
|
|
||||||
_effectImage = effectImage;
|
|
||||||
_canPurchase = canPurchase;
|
_canPurchase = canPurchase;
|
||||||
_payableGold = payableGold;
|
_payableGold = payableGold;
|
||||||
}
|
}
|
||||||
@ -304,14 +266,10 @@ class SCVipLevelConfigRes {
|
|||||||
_durationDays = _numValue(map['durationDays']);
|
_durationDays = _numValue(map['durationDays']);
|
||||||
_priceGold = _numValue(map['priceGold']);
|
_priceGold = _numValue(map['priceGold']);
|
||||||
_badge = _resourceValue(map['badge']);
|
_badge = _resourceValue(map['badge']);
|
||||||
_longBadge = _resourceValue(map['longBadge']) ?? _badge;
|
|
||||||
_shortBadge = _resourceValue(map['shortBadge']);
|
|
||||||
_avatarFrame = _resourceValue(map['avatarFrame']);
|
_avatarFrame = _resourceValue(map['avatarFrame']);
|
||||||
_entryEffect = _resourceValue(map['entryEffect']);
|
_entryEffect = _resourceValue(map['entryEffect']);
|
||||||
_chatBubble = _resourceValue(map['chatBubble']);
|
_chatBubble = _resourceValue(map['chatBubble']);
|
||||||
_floatPicture = _resourceValue(map['floatPicture']);
|
_floatPicture = _resourceValue(map['floatPicture']);
|
||||||
_backgroundCard = _resourceValue(map['backgroundCard']);
|
|
||||||
_effectImage = _resourceValue(map['effectImage']);
|
|
||||||
_canPurchase = _boolValue(map['canPurchase']);
|
_canPurchase = _boolValue(map['canPurchase']);
|
||||||
_payableGold = _numValue(map['payableGold']);
|
_payableGold = _numValue(map['payableGold']);
|
||||||
}
|
}
|
||||||
@ -323,14 +281,10 @@ class SCVipLevelConfigRes {
|
|||||||
num? _durationDays;
|
num? _durationDays;
|
||||||
num? _priceGold;
|
num? _priceGold;
|
||||||
SCVipResourceRes? _badge;
|
SCVipResourceRes? _badge;
|
||||||
SCVipResourceRes? _longBadge;
|
|
||||||
SCVipResourceRes? _shortBadge;
|
|
||||||
SCVipResourceRes? _avatarFrame;
|
SCVipResourceRes? _avatarFrame;
|
||||||
SCVipResourceRes? _entryEffect;
|
SCVipResourceRes? _entryEffect;
|
||||||
SCVipResourceRes? _chatBubble;
|
SCVipResourceRes? _chatBubble;
|
||||||
SCVipResourceRes? _floatPicture;
|
SCVipResourceRes? _floatPicture;
|
||||||
SCVipResourceRes? _backgroundCard;
|
|
||||||
SCVipResourceRes? _effectImage;
|
|
||||||
bool? _canPurchase;
|
bool? _canPurchase;
|
||||||
num? _payableGold;
|
num? _payableGold;
|
||||||
|
|
||||||
@ -346,11 +300,7 @@ class SCVipLevelConfigRes {
|
|||||||
|
|
||||||
num? get priceGold => _priceGold;
|
num? get priceGold => _priceGold;
|
||||||
|
|
||||||
SCVipResourceRes? get badge => longBadge;
|
SCVipResourceRes? get badge => _badge;
|
||||||
|
|
||||||
SCVipResourceRes? get longBadge => _longBadge ?? _badge;
|
|
||||||
|
|
||||||
SCVipResourceRes? get shortBadge => _shortBadge;
|
|
||||||
|
|
||||||
SCVipResourceRes? get avatarFrame => _avatarFrame;
|
SCVipResourceRes? get avatarFrame => _avatarFrame;
|
||||||
|
|
||||||
@ -360,10 +310,6 @@ class SCVipLevelConfigRes {
|
|||||||
|
|
||||||
SCVipResourceRes? get floatPicture => _floatPicture;
|
SCVipResourceRes? get floatPicture => _floatPicture;
|
||||||
|
|
||||||
SCVipResourceRes? get backgroundCard => _backgroundCard;
|
|
||||||
|
|
||||||
SCVipResourceRes? get effectImage => _effectImage;
|
|
||||||
|
|
||||||
bool? get canPurchase => _canPurchase;
|
bool? get canPurchase => _canPurchase;
|
||||||
|
|
||||||
num? get payableGold => _payableGold;
|
num? get payableGold => _payableGold;
|
||||||
@ -386,17 +332,11 @@ class SCVipLevelConfigRes {
|
|||||||
map['enabled'] = _enabled;
|
map['enabled'] = _enabled;
|
||||||
map['durationDays'] = _durationDays;
|
map['durationDays'] = _durationDays;
|
||||||
map['priceGold'] = _priceGold;
|
map['priceGold'] = _priceGold;
|
||||||
if (badge != null) map['badge'] = badge?.toJson();
|
if (_badge != null) map['badge'] = _badge?.toJson();
|
||||||
if (longBadge != null) map['longBadge'] = longBadge?.toJson();
|
|
||||||
if (_shortBadge != null) map['shortBadge'] = _shortBadge?.toJson();
|
|
||||||
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
|
if (_avatarFrame != null) map['avatarFrame'] = _avatarFrame?.toJson();
|
||||||
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
|
if (_entryEffect != null) map['entryEffect'] = _entryEffect?.toJson();
|
||||||
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
|
if (_chatBubble != null) map['chatBubble'] = _chatBubble?.toJson();
|
||||||
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
|
if (_floatPicture != null) map['floatPicture'] = _floatPicture?.toJson();
|
||||||
if (_backgroundCard != null) {
|
|
||||||
map['backgroundCard'] = _backgroundCard?.toJson();
|
|
||||||
}
|
|
||||||
if (_effectImage != null) map['effectImage'] = _effectImage?.toJson();
|
|
||||||
map['canPurchase'] = _canPurchase;
|
map['canPurchase'] = _canPurchase;
|
||||||
map['payableGold'] = _payableGold;
|
map['payableGold'] = _payableGold;
|
||||||
return map;
|
return map;
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
// ignore_for_file: constant_identifier_names
|
enum SCPropsType {
|
||||||
|
AVATAR_FRAME,
|
||||||
enum SCPropsType {
|
FLOAT_PICTURE,
|
||||||
AVATAR_FRAME,
|
DATA_CARD,
|
||||||
FLOAT_PICTURE,
|
CHAT_BUBBLE,
|
||||||
DATA_CARD,
|
RIDE,
|
||||||
CHAT_BUBBLE,
|
LAYOUT,
|
||||||
RIDE,
|
THEME,
|
||||||
LAYOUT,
|
BADGE,
|
||||||
THEME,
|
NOBLE_VIP,
|
||||||
BADGE,
|
FRAGMENTS,
|
||||||
NOBLE_VIP,
|
RED_PACKET,
|
||||||
VIP_EFFECT_IMAGE,
|
}
|
||||||
FRAGMENTS,
|
|
||||||
RED_PACKET,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -57,18 +57,15 @@ Widget head({
|
|||||||
final String headdressCoverUrl = headdressCover?.trim() ?? "";
|
final String headdressCoverUrl = headdressCover?.trim() ?? "";
|
||||||
final String headdressExtension =
|
final String headdressExtension =
|
||||||
SCPathUtils.getFileExtension(headdressUrl).toLowerCase();
|
SCPathUtils.getFileExtension(headdressUrl).toLowerCase();
|
||||||
final bool hasLocalVirtualHeaddress = _isLocalAssetsVirtualUrl(headdressUrl);
|
|
||||||
final bool hasPictureHeaddress =
|
final bool hasPictureHeaddress =
|
||||||
headdressUrl.isNotEmpty && SCPathUtils.fileTypeIsPic2(headdressUrl);
|
headdressUrl.isNotEmpty && SCPathUtils.fileTypeIsPic2(headdressUrl);
|
||||||
final bool hasAnimatedHeaddress =
|
final bool hasAnimatedHeaddress =
|
||||||
headdressExtension == ".svga" || headdressExtension == ".mp4";
|
headdressExtension == ".svga" || headdressExtension == ".mp4";
|
||||||
final bool shouldShowAnimatedHeaddress =
|
final bool shouldShowAnimatedHeaddress =
|
||||||
hasAnimatedHeaddress &&
|
hasAnimatedHeaddress && SCGlobalConfig.allowsHighCostAnimations;
|
||||||
SCGlobalConfig.allowsHighCostAnimations &&
|
|
||||||
!hasLocalVirtualHeaddress;
|
|
||||||
final bool shouldShowStaticHeaddressFallback =
|
final bool shouldShowStaticHeaddressFallback =
|
||||||
hasAnimatedHeaddress &&
|
hasAnimatedHeaddress &&
|
||||||
(!SCGlobalConfig.allowsHighCostAnimations || hasLocalVirtualHeaddress) &&
|
!SCGlobalConfig.allowsHighCostAnimations &&
|
||||||
headdressCoverUrl.isNotEmpty;
|
headdressCoverUrl.isNotEmpty;
|
||||||
final bool hasHeaddress =
|
final bool hasHeaddress =
|
||||||
headdressUrl.isNotEmpty || shouldShowStaticHeaddressFallback;
|
headdressUrl.isNotEmpty || shouldShowStaticHeaddressFallback;
|
||||||
@ -208,11 +205,6 @@ Widget head({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isLocalAssetsVirtualUrl(String value) {
|
|
||||||
final uri = Uri.tryParse(value.trim());
|
|
||||||
return uri?.host.toLowerCase() == "local.assets";
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget netImage({
|
Widget netImage({
|
||||||
required String url,
|
required String url,
|
||||||
String? defaultImg,
|
String? defaultImg,
|
||||||
|
|||||||
@ -23,8 +23,6 @@ class SCDataCardResourceView extends StatelessWidget {
|
|||||||
this.fit = BoxFit.cover,
|
this.fit = BoxFit.cover,
|
||||||
this.loop = true,
|
this.loop = true,
|
||||||
this.fallback,
|
this.fallback,
|
||||||
this.showCoverFallback = true,
|
|
||||||
this.allowCoverAsResource = true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final PropsResources? resource;
|
final PropsResources? resource;
|
||||||
@ -35,8 +33,6 @@ class SCDataCardResourceView extends StatelessWidget {
|
|||||||
final BoxFit fit;
|
final BoxFit fit;
|
||||||
final bool loop;
|
final bool loop;
|
||||||
final Widget? fallback;
|
final Widget? fallback;
|
||||||
final bool showCoverFallback;
|
|
||||||
final bool allowCoverAsResource;
|
|
||||||
|
|
||||||
static bool isSupported(String? url) {
|
static bool isSupported(String? url) {
|
||||||
return _isSvga(url) ||
|
return _isSvga(url) ||
|
||||||
@ -54,22 +50,15 @@ class SCDataCardResourceView extends StatelessWidget {
|
|||||||
final resolvedHeight = _resolvedSize(height, constraints.maxHeight);
|
final resolvedHeight = _resolvedSize(height, constraints.maxHeight);
|
||||||
final cover = _firstNonBlank([coverUrl, resource?.cover]);
|
final cover = _firstNonBlank([coverUrl, resource?.cover]);
|
||||||
final source = _firstNonBlank([sourceUrl, resource?.sourceUrl]);
|
final source = _firstNonBlank([sourceUrl, resource?.sourceUrl]);
|
||||||
final resourceUrl =
|
final resourceUrl = source.isNotEmpty ? source : cover;
|
||||||
source.isNotEmpty || !allowCoverAsResource ? source : cover;
|
|
||||||
final emptyFallback = SizedBox(
|
|
||||||
width: resolvedWidth,
|
|
||||||
height: resolvedHeight,
|
|
||||||
);
|
|
||||||
final fallbackView =
|
final fallbackView =
|
||||||
fallback ??
|
fallback ??
|
||||||
(showCoverFallback
|
_buildImage(
|
||||||
? _buildImage(
|
cover,
|
||||||
cover,
|
width: resolvedWidth,
|
||||||
width: resolvedWidth,
|
height: resolvedHeight,
|
||||||
height: resolvedHeight,
|
fit: fit,
|
||||||
fit: fit,
|
);
|
||||||
)
|
|
||||||
: emptyFallback);
|
|
||||||
|
|
||||||
if (resourceUrl.isEmpty) {
|
if (resourceUrl.isEmpty) {
|
||||||
return fallbackView;
|
return fallbackView;
|
||||||
@ -341,12 +330,10 @@ class _LoopingDataCardVapVideo extends StatefulWidget {
|
|||||||
|
|
||||||
class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
||||||
static const int _legacyRgbLeftAlphaRightMode = 3;
|
static const int _legacyRgbLeftAlphaRightMode = 3;
|
||||||
static const int _longRunningLoopCount = 999999;
|
|
||||||
|
|
||||||
VapController? _controller;
|
VapController? _controller;
|
||||||
int _playToken = 0;
|
int _playToken = 0;
|
||||||
bool _failed = false;
|
bool _failed = false;
|
||||||
bool _started = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant _LoopingDataCardVapVideo oldWidget) {
|
void didUpdateWidget(covariant _LoopingDataCardVapVideo oldWidget) {
|
||||||
@ -356,11 +343,8 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
|||||||
oldWidget.fit != widget.fit) {
|
oldWidget.fit != widget.fit) {
|
||||||
_playToken++;
|
_playToken++;
|
||||||
_stop();
|
_stop();
|
||||||
if (_failed || _started) {
|
if (_failed) {
|
||||||
setState(() {
|
setState(() => _failed = false);
|
||||||
_failed = false;
|
|
||||||
_started = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_play();
|
_play();
|
||||||
}
|
}
|
||||||
@ -378,32 +362,15 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
|||||||
_controller = controller;
|
_controller = controller;
|
||||||
controller.setAnimListener(
|
controller.setAnimListener(
|
||||||
onFailed: (_, __, ___) => _markFailed(),
|
onFailed: (_, __, ___) => _markFailed(),
|
||||||
onVideoComplete: _handleVideoComplete,
|
|
||||||
onVideoStart: () {
|
onVideoStart: () {
|
||||||
if (mounted && (_failed || !_started)) {
|
if (mounted && _failed) {
|
||||||
setState(() {
|
setState(() => _failed = false);
|
||||||
_failed = false;
|
|
||||||
_started = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
_play();
|
_play();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleVideoComplete() {
|
|
||||||
if (!mounted || !widget.loop || _failed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unawaited(
|
|
||||||
Future<void>.delayed(Duration.zero, () {
|
|
||||||
if (mounted && widget.loop && !_failed) {
|
|
||||||
return _play();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _play() async {
|
Future<void> _play() async {
|
||||||
final controller = _controller;
|
final controller = _controller;
|
||||||
final url = widget.url.trim();
|
final url = widget.url.trim();
|
||||||
@ -423,7 +390,7 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
|||||||
await controller.setMute(true);
|
await controller.setMute(true);
|
||||||
await controller.enableVersion1(true);
|
await controller.enableVersion1(true);
|
||||||
await controller.setVideoMode(_legacyRgbLeftAlphaRightMode);
|
await controller.setVideoMode(_legacyRgbLeftAlphaRightMode);
|
||||||
await controller.setLoop(widget.loop ? _longRunningLoopCount : 0);
|
await controller.setLoop(widget.loop ? -1 : 0);
|
||||||
await controller.setScaleType(_scaleTypeForFit(widget.fit).key);
|
await controller.setScaleType(_scaleTypeForFit(widget.fit).key);
|
||||||
if (!_isCurrent(token, controller)) {
|
if (!_isCurrent(token, controller)) {
|
||||||
return;
|
return;
|
||||||
@ -484,10 +451,7 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
|||||||
if (!mounted || _failed) {
|
if (!mounted || _failed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() => _failed = true);
|
||||||
_failed = true;
|
|
||||||
_started = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScaleType _scaleTypeForFit(BoxFit fit) {
|
ScaleType _scaleTypeForFit(BoxFit fit) {
|
||||||
@ -518,7 +482,7 @@ class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> {
|
|||||||
if (!_failed)
|
if (!_failed)
|
||||||
VapView(
|
VapView(
|
||||||
scaleType: _scaleTypeForFit(widget.fit),
|
scaleType: _scaleTypeForFit(widget.fit),
|
||||||
repeat: widget.loop ? _longRunningLoopCount : 0,
|
repeat: widget.loop ? -1 : 0,
|
||||||
mute: true,
|
mute: true,
|
||||||
onViewCreated: _handleViewCreated,
|
onViewCreated: _handleViewCreated,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -209,20 +209,6 @@ class RoomRocketDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
String debugRoomRocketTitleText({required int level, required int round}) {
|
|
||||||
return _roomRocketTitleText(level: level, round: round);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _roomRocketTitleText({required int level, required int round}) {
|
|
||||||
final normalizedLevel = level.clamp(1, 99).toInt();
|
|
||||||
final normalizedRound = round.clamp(1, 99).toInt();
|
|
||||||
if (normalizedRound <= 1) {
|
|
||||||
return 'Lv.$normalizedLevel Today';
|
|
||||||
}
|
|
||||||
return 'Lv.$normalizedLevel · Round $normalizedRound Today';
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomRocketRewardItem {
|
class RoomRocketRewardItem {
|
||||||
const RoomRocketRewardItem({
|
const RoomRocketRewardItem({
|
||||||
this.asset,
|
this.asset,
|
||||||
@ -463,7 +449,7 @@ class _RocketCanvas extends StatelessWidget {
|
|||||||
top: s(120),
|
top: s(120),
|
||||||
width: s(_designWidth),
|
width: s(_designWidth),
|
||||||
child: _GradientText(
|
child: _GradientText(
|
||||||
_roomRocketTitleText(level: selectedLevelItem.level, round: round),
|
'Round $round Today',
|
||||||
fontSize: s(16),
|
fontSize: s(16),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -477,11 +463,31 @@ class _RocketCanvas extends StatelessWidget {
|
|||||||
top: s(212),
|
top: s(212),
|
||||||
width: s(40),
|
width: s(40),
|
||||||
height: s(190),
|
height: s(190),
|
||||||
child: _LevelRail(
|
child: ScrollConfiguration(
|
||||||
scale: scale,
|
behavior: const _NoGlowScrollBehavior(),
|
||||||
levels: levels,
|
child: ListView.separated(
|
||||||
selectedLevel: selectedLevel,
|
padding: EdgeInsets.zero,
|
||||||
onLevelSelected: onLevelSelected,
|
primary: false,
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
itemCount: levels.length,
|
||||||
|
separatorBuilder: (_, __) => SizedBox(height: s(4)),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = levels[index];
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: item.enabled ? () => onLevelSelected?.call(index) : null,
|
||||||
|
child: SizedBox(
|
||||||
|
width: s(40),
|
||||||
|
height: s(52),
|
||||||
|
child: _LevelBadge(
|
||||||
|
scale: scale,
|
||||||
|
item: item,
|
||||||
|
selected: index == selectedLevel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1025,12 +1031,9 @@ class _LevelBadge extends StatelessWidget {
|
|||||||
item.label,
|
item.label,
|
||||||
fontSize: 10 * scale,
|
fontSize: 10 * scale,
|
||||||
color:
|
color:
|
||||||
selected
|
item.enabled
|
||||||
? const Color(0xFFA5FFE3)
|
|
||||||
: item.enabled
|
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: Colors.white.withValues(alpha: 0.52),
|
: Colors.white.withValues(alpha: 0.52),
|
||||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -1039,118 +1042,6 @@ class _LevelBadge extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LevelRail extends StatefulWidget {
|
|
||||||
const _LevelRail({
|
|
||||||
required this.scale,
|
|
||||||
required this.levels,
|
|
||||||
required this.selectedLevel,
|
|
||||||
this.onLevelSelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
final double scale;
|
|
||||||
final List<RoomRocketLevelItem> levels;
|
|
||||||
final int selectedLevel;
|
|
||||||
final ValueChanged<int>? onLevelSelected;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_LevelRail> createState() => _LevelRailState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LevelRailState extends State<_LevelRail> {
|
|
||||||
late final ScrollController _controller;
|
|
||||||
|
|
||||||
double get _itemHeight => 52 * widget.scale;
|
|
||||||
double get _separatorHeight => 4 * widget.scale;
|
|
||||||
double get _viewportHeight => 190 * widget.scale;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller = ScrollController(
|
|
||||||
initialScrollOffset: _targetOffset(widget.selectedLevel),
|
|
||||||
);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToSelected());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant _LevelRail oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (oldWidget.selectedLevel != widget.selectedLevel ||
|
|
||||||
oldWidget.levels.length != widget.levels.length ||
|
|
||||||
oldWidget.scale != widget.scale) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToSelected());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
double _targetOffset(int selectedLevel) {
|
|
||||||
if (widget.levels.isEmpty) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final index = selectedLevel.clamp(0, widget.levels.length - 1).toInt();
|
|
||||||
final totalHeight =
|
|
||||||
widget.levels.length * _itemHeight +
|
|
||||||
math.max(0, widget.levels.length - 1) * _separatorHeight;
|
|
||||||
final maxOffset = math.max(0, totalHeight - _viewportHeight).toDouble();
|
|
||||||
final centered =
|
|
||||||
index * (_itemHeight + _separatorHeight) -
|
|
||||||
(_viewportHeight - _itemHeight) / 2;
|
|
||||||
return centered.clamp(0, maxOffset).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scrollToSelected() {
|
|
||||||
if (!mounted || !_controller.hasClients) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final target = _targetOffset(widget.selectedLevel);
|
|
||||||
if ((_controller.offset - target).abs() < 0.5) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_controller.animateTo(
|
|
||||||
target,
|
|
||||||
duration: const Duration(milliseconds: 220),
|
|
||||||
curve: Curves.easeOutCubic,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ScrollConfiguration(
|
|
||||||
behavior: const _NoGlowScrollBehavior(),
|
|
||||||
child: ListView.separated(
|
|
||||||
controller: _controller,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
primary: false,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
itemCount: widget.levels.length,
|
|
||||||
separatorBuilder: (_, __) => SizedBox(height: _separatorHeight),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final item = widget.levels[index];
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap:
|
|
||||||
item.enabled ? () => widget.onLevelSelected?.call(index) : null,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 40 * widget.scale,
|
|
||||||
height: _itemHeight,
|
|
||||||
child: _LevelBadge(
|
|
||||||
scale: widget.scale,
|
|
||||||
item: item,
|
|
||||||
selected: index == widget.selectedLevel,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CrewAvatar extends StatelessWidget {
|
class _CrewAvatar extends StatelessWidget {
|
||||||
const _CrewAvatar({
|
const _CrewAvatar({
|
||||||
required this.scale,
|
required this.scale,
|
||||||
@ -1508,45 +1399,28 @@ class _LevelBadgePainter extends CustomPainter {
|
|||||||
size,
|
size,
|
||||||
const Size(40, 52),
|
const Size(40, 52),
|
||||||
);
|
);
|
||||||
if (selected) {
|
|
||||||
canvas.drawPath(
|
|
||||||
path,
|
|
||||||
Paint()
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeWidth = size.width / 7
|
|
||||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, size.width / 12)
|
|
||||||
..color = const Color(0xFF56FFD0).withValues(alpha: 0.38),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
path,
|
path,
|
||||||
Paint()
|
Paint()
|
||||||
..shader = LinearGradient(
|
..shader = const LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors:
|
colors: [Color(0xFF077C60), Color(0xFF08251E)],
|
||||||
selected
|
|
||||||
? const [Color(0xFF10DFA8), Color(0xFF07392E)]
|
|
||||||
: const [Color(0xFF077C60), Color(0xFF08251E)],
|
|
||||||
).createShader(Offset.zero & size),
|
).createShader(Offset.zero & size),
|
||||||
);
|
);
|
||||||
canvas.drawPath(
|
canvas.drawPath(
|
||||||
path,
|
path,
|
||||||
Paint()
|
Paint()
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = selected ? size.width / 14 : size.width / 40
|
..strokeWidth = selected ? size.width / 22 : size.width / 40
|
||||||
..shader = LinearGradient(
|
..shader = LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [
|
colors: [
|
||||||
selected
|
enabled
|
||||||
? Colors.white
|
|
||||||
: enabled
|
|
||||||
? const Color(0xFFB2FBCC)
|
? const Color(0xFFB2FBCC)
|
||||||
: const Color(0xFFB2FBCC).withValues(alpha: 0.42),
|
: const Color(0xFFB2FBCC).withValues(alpha: 0.42),
|
||||||
selected
|
const Color(0xFFB2FBCC).withValues(alpha: 0),
|
||||||
? const Color(0xFF56FFD0)
|
|
||||||
: const Color(0xFFB2FBCC).withValues(alpha: 0),
|
|
||||||
],
|
],
|
||||||
).createShader(Offset.zero & size),
|
).createShader(Offset.zero & size),
|
||||||
);
|
);
|
||||||
@ -1680,14 +1554,12 @@ class _RocketText extends StatelessWidget {
|
|||||||
required this.fontSize,
|
required this.fontSize,
|
||||||
required this.color,
|
required this.color,
|
||||||
this.textAlign = TextAlign.center,
|
this.textAlign = TextAlign.center,
|
||||||
this.fontWeight = FontWeight.w400,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String text;
|
final String text;
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
final Color color;
|
final Color color;
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
final FontWeight fontWeight;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -1700,7 +1572,6 @@ class _RocketText extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: color,
|
color: color,
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
fontWeight: fontWeight,
|
|
||||||
height: 1,
|
height: 1,
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -34,24 +34,19 @@ class RoomRocketFloatingEntry extends StatelessWidget {
|
|||||||
return Selector<RtcProvider, SCRoomRocketStatusRes?>(
|
return Selector<RtcProvider, SCRoomRocketStatusRes?>(
|
||||||
selector: (_, provider) => provider.roomRocketStatus,
|
selector: (_, provider) => provider.roomRocketStatus,
|
||||||
builder: (context, status, child) {
|
builder: (context, status, child) {
|
||||||
if (status == null || !status.isEnabled) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
final level = _currentEntryLevel(status);
|
final level = _currentEntryLevel(status);
|
||||||
if (level == null || level.rocketIconUrl.trim().isEmpty) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
final percent = _entryPercent(status);
|
final percent = _entryPercent(status);
|
||||||
final shouldShake =
|
final shouldShake =
|
||||||
(status.shake ?? false) || percent > level.shakeThresholdPercent;
|
(status?.shake ?? false) ||
|
||||||
|
(level != null && percent > level.shakeThresholdPercent);
|
||||||
return SCDebounceWidget(
|
return SCDebounceWidget(
|
||||||
onTap: () => _showRocketDialog(context, status),
|
onTap: () => _showRocketDialog(context, status),
|
||||||
child: _RoomRocketEntryButton(
|
child: _RoomRocketEntryButton(
|
||||||
iconUrl: level.rocketIconUrl,
|
iconUrl: level?.rocketIconUrl ?? '',
|
||||||
progressUrl: level.progressBarUrl,
|
progressUrl: level?.progressBarUrl ?? '',
|
||||||
progressPercent: percent,
|
progressPercent: percent,
|
||||||
shake: shouldShake,
|
shake: shouldShake,
|
||||||
loading: false,
|
loading: status == null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -113,55 +108,6 @@ class RoomRocketFloatingEntry extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
int debugResolveRoomRocketSelectedLevelIndex({
|
|
||||||
required List<RoomRocketLevelItem>? levelItems,
|
|
||||||
required SCRoomRocketStatusRes? status,
|
|
||||||
required int selectedLevel,
|
|
||||||
required bool levelTouched,
|
|
||||||
}) {
|
|
||||||
return _resolveRoomRocketSelectedLevelIndex(
|
|
||||||
levelItems: levelItems,
|
|
||||||
status: status,
|
|
||||||
selectedLevel: selectedLevel,
|
|
||||||
levelTouched: levelTouched,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _resolveRoomRocketSelectedLevelIndex({
|
|
||||||
required List<RoomRocketLevelItem>? levelItems,
|
|
||||||
required SCRoomRocketStatusRes? status,
|
|
||||||
required int selectedLevel,
|
|
||||||
required bool levelTouched,
|
|
||||||
}) {
|
|
||||||
final items = levelItems ?? const <RoomRocketLevelItem>[];
|
|
||||||
final maxIndex = math.max(0, (items.isEmpty ? 6 : items.length) - 1);
|
|
||||||
if (levelTouched) {
|
|
||||||
return selectedLevel.clamp(0, maxIndex).toInt();
|
|
||||||
}
|
|
||||||
final currentLevel = _roomRocketLevelNumberFromStatus(status);
|
|
||||||
final matchedIndex = items.indexWhere((item) => item.level == currentLevel);
|
|
||||||
if (matchedIndex >= 0) {
|
|
||||||
return matchedIndex;
|
|
||||||
}
|
|
||||||
return (currentLevel - 1).clamp(0, maxIndex).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _roomRocketLevelNumberFromStatus(SCRoomRocketStatusRes? status) {
|
|
||||||
return (status?.currentLevel ?? status?.level ?? 1).clamp(1, 99).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _roomRocketLevelNumberForIndex(
|
|
||||||
List<RoomRocketLevelItem>? levelItems,
|
|
||||||
int index,
|
|
||||||
) {
|
|
||||||
final items = levelItems ?? const <RoomRocketLevelItem>[];
|
|
||||||
if (index >= 0 && index < items.length) {
|
|
||||||
return items[index].level.clamp(1, 99).toInt();
|
|
||||||
}
|
|
||||||
return (index + 1).clamp(1, 99).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RoomRocketEntryButton extends StatefulWidget {
|
class _RoomRocketEntryButton extends StatefulWidget {
|
||||||
const _RoomRocketEntryButton({
|
const _RoomRocketEntryButton({
|
||||||
required this.iconUrl,
|
required this.iconUrl,
|
||||||
@ -423,16 +369,9 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_status = widget.initialStatus;
|
_status = widget.initialStatus;
|
||||||
_selectedLevel = _levelIndexFromStatus(widget.initialStatus);
|
_selectedLevel = _levelIndexFromStatus(widget.initialStatus);
|
||||||
final initialLevelItems = RoomRocketApiMapper.levelsFromStatus(
|
_cacheRewardGroupsFromStatus(widget.initialStatus, _selectedLevel + 1);
|
||||||
widget.initialStatus,
|
|
||||||
);
|
|
||||||
final initialLevelNumber = _levelNumberForIndex(
|
|
||||||
initialLevelItems,
|
|
||||||
_selectedLevel,
|
|
||||||
);
|
|
||||||
_cacheRewardGroupsFromStatus(widget.initialStatus, initialLevelNumber);
|
|
||||||
unawaited(_load());
|
unawaited(_load());
|
||||||
unawaited(_loadRewardForLevel(initialLevelNumber));
|
unawaited(_loadRewardForLevel(_selectedLevel + 1));
|
||||||
unawaited(_loadConfigs());
|
unawaited(_loadConfigs());
|
||||||
unawaited(_loadRule());
|
unawaited(_loadRule());
|
||||||
}
|
}
|
||||||
@ -448,29 +387,16 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!res.isEnabled) {
|
|
||||||
try {
|
|
||||||
Provider.of<RtcProvider>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).updateRoomRocketConfigurationStatus(res);
|
|
||||||
} catch (_) {}
|
|
||||||
widget.onClose?.call();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_debugStatusAssets(res);
|
_debugStatusAssets(res);
|
||||||
final levelItems = RoomRocketApiMapper.levelsFromStatus(res);
|
|
||||||
late final int selectedLevelNumber;
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_status = res;
|
_status = res;
|
||||||
if (!_levelTouched) {
|
if (!_levelTouched) {
|
||||||
_selectedLevel = _levelIndexFromStatus(res);
|
_selectedLevel = _levelIndexFromStatus(res);
|
||||||
}
|
}
|
||||||
selectedLevelNumber = _levelNumberForIndex(levelItems, _selectedLevel);
|
_cacheRewardGroupsFromStatus(res, _selectedLevel + 1);
|
||||||
_cacheRewardGroupsFromStatus(res, selectedLevelNumber);
|
|
||||||
});
|
});
|
||||||
unawaited(_loadCrewForLevel(selectedLevelNumber));
|
unawaited(_loadCrewForLevel(_selectedLevel + 1));
|
||||||
unawaited(_loadRewardForLevel(selectedLevelNumber));
|
unawaited(_loadRewardForLevel(_selectedLevel + 1));
|
||||||
try {
|
try {
|
||||||
Provider.of<RtcProvider>(
|
Provider.of<RtcProvider>(
|
||||||
context,
|
context,
|
||||||
@ -504,29 +430,13 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!contents.enabled) {
|
|
||||||
setState(() {
|
|
||||||
_levelContents = contents;
|
|
||||||
_configRewardGroupsByLevel = {};
|
|
||||||
});
|
|
||||||
_rocketUiDebug('rocket level contents disabled');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final groups = RoomRocketApiMapper.rewardGroupsByLevelFromLevelContents(
|
final groups = RoomRocketApiMapper.rewardGroupsByLevelFromLevelContents(
|
||||||
contents,
|
contents,
|
||||||
);
|
);
|
||||||
final levelItems = RoomRocketApiMapper.levelsFromLevelContents(contents);
|
|
||||||
late final int selectedLevelNumber;
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_levelContents = contents;
|
_levelContents = contents;
|
||||||
_configRewardGroupsByLevel = groups;
|
_configRewardGroupsByLevel = groups;
|
||||||
if (!_levelTouched) {
|
|
||||||
_selectedLevel = _resolvedSelectedLevelIndex(levelItems, _status);
|
|
||||||
}
|
|
||||||
selectedLevelNumber = _levelNumberForIndex(levelItems, _selectedLevel);
|
|
||||||
});
|
});
|
||||||
unawaited(_loadCrewForLevel(selectedLevelNumber));
|
|
||||||
unawaited(_loadRewardForLevel(selectedLevelNumber));
|
|
||||||
_rocketUiDebug(
|
_rocketUiDebug(
|
||||||
'rocket level contents loaded levels=${contents.levels.length} '
|
'rocket level contents loaded levels=${contents.levels.length} '
|
||||||
'rewardLevels=${groups.keys.toList()}',
|
'rewardLevels=${groups.keys.toList()}',
|
||||||
@ -561,11 +471,8 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final status = _status;
|
final status = _status;
|
||||||
final levelItems =
|
final selectedLevel = _selectedLevel.clamp(0, 5).toInt();
|
||||||
RoomRocketApiMapper.levelsFromLevelContents(_levelContents) ??
|
final selectedLevelNumber = selectedLevel + 1;
|
||||||
RoomRocketApiMapper.levelsFromStatus(status);
|
|
||||||
final selectedLevel = _resolvedSelectedLevelIndex(levelItems, status);
|
|
||||||
final selectedLevelNumber = _levelNumberForIndex(levelItems, selectedLevel);
|
|
||||||
final currentLevelNumber = _levelNumberFromStatus(status);
|
final currentLevelNumber = _levelNumberFromStatus(status);
|
||||||
final statusRewardGroups = RoomRocketApiMapper.rewardGroupsFromStatus(
|
final statusRewardGroups = RoomRocketApiMapper.rewardGroupsFromStatus(
|
||||||
status,
|
status,
|
||||||
@ -580,6 +487,9 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
: rewardGroupsFromStatus ??
|
: rewardGroupsFromStatus ??
|
||||||
configRewardGroups ??
|
configRewardGroups ??
|
||||||
const RoomRocketRewardGroups();
|
const RoomRocketRewardGroups();
|
||||||
|
final levelItems =
|
||||||
|
RoomRocketApiMapper.levelsFromLevelContents(_levelContents) ??
|
||||||
|
RoomRocketApiMapper.levelsFromStatus(status);
|
||||||
return RoomRocketDialog(
|
return RoomRocketDialog(
|
||||||
status: status,
|
status: status,
|
||||||
levels: levelItems,
|
levels: levelItems,
|
||||||
@ -626,32 +536,13 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int _levelIndexFromStatus(SCRoomRocketStatusRes? status) {
|
int _levelIndexFromStatus(SCRoomRocketStatusRes? status) {
|
||||||
return _resolveRoomRocketSelectedLevelIndex(
|
return ((status?.currentLevel ?? status?.level ?? 1) - 1)
|
||||||
levelItems: RoomRocketApiMapper.levelsFromStatus(status),
|
.clamp(0, 5)
|
||||||
status: status,
|
.toInt();
|
||||||
selectedLevel: _selectedLevel,
|
|
||||||
levelTouched: false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _levelNumberFromStatus(SCRoomRocketStatusRes? status) {
|
int _levelNumberFromStatus(SCRoomRocketStatusRes? status) {
|
||||||
return _roomRocketLevelNumberFromStatus(status);
|
return (status?.currentLevel ?? status?.level ?? 1).clamp(1, 99).toInt();
|
||||||
}
|
|
||||||
|
|
||||||
int _resolvedSelectedLevelIndex(
|
|
||||||
List<RoomRocketLevelItem>? levelItems,
|
|
||||||
SCRoomRocketStatusRes? status,
|
|
||||||
) {
|
|
||||||
return _resolveRoomRocketSelectedLevelIndex(
|
|
||||||
levelItems: levelItems,
|
|
||||||
status: status,
|
|
||||||
selectedLevel: _selectedLevel,
|
|
||||||
levelTouched: _levelTouched,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _levelNumberForIndex(List<RoomRocketLevelItem>? levelItems, int index) {
|
|
||||||
return _roomRocketLevelNumberForIndex(levelItems, index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cacheRewardGroupsFromStatus(SCRoomRocketStatusRes? status, int level) {
|
void _cacheRewardGroupsFromStatus(SCRoomRocketStatusRes? status, int level) {
|
||||||
@ -685,17 +576,13 @@ class _RoomRocketDialogLoaderState extends State<_RoomRocketDialogLoader> {
|
|||||||
|
|
||||||
void _onLevelSelected(int index) {
|
void _onLevelSelected(int index) {
|
||||||
final selectedIndex = index.clamp(0, 5).toInt();
|
final selectedIndex = index.clamp(0, 5).toInt();
|
||||||
final levelItems =
|
_rocketUiDebug('select rocket level=${selectedIndex + 1}');
|
||||||
RoomRocketApiMapper.levelsFromLevelContents(_levelContents) ??
|
|
||||||
RoomRocketApiMapper.levelsFromStatus(_status);
|
|
||||||
final selectedLevelNumber = _levelNumberForIndex(levelItems, selectedIndex);
|
|
||||||
_rocketUiDebug('select rocket level=$selectedLevelNumber');
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_levelTouched = true;
|
_levelTouched = true;
|
||||||
_selectedLevel = selectedIndex;
|
_selectedLevel = selectedIndex;
|
||||||
});
|
});
|
||||||
unawaited(_loadRewardForLevel(selectedLevelNumber));
|
unawaited(_loadRewardForLevel(selectedIndex + 1));
|
||||||
unawaited(_loadCrewForLevel(selectedLevelNumber));
|
unawaited(_loadCrewForLevel(selectedIndex + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadRewardForLevel(int level) async {
|
Future<void> _loadRewardForLevel(int level) async {
|
||||||
@ -914,75 +801,60 @@ class _RoomRocketRecordDialogLoaderState
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
unawaited(_loadAllRecords());
|
unawaited(_load(reset: true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadAllRecords() async {
|
Future<void> _load({bool reset = false}) async {
|
||||||
if (_loadingMore) {
|
if (_loadingMore || (!reset && (!_hasMore || _loading))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final requestCursor = reset ? 1 : _cursor;
|
||||||
setState(() {
|
setState(() {
|
||||||
_loading = true;
|
if (reset) {
|
||||||
_loadingMore = false;
|
_loading = true;
|
||||||
_hasMore = true;
|
} else {
|
||||||
_cursor = 1;
|
_loadingMore = true;
|
||||||
_rawRecords.clear();
|
|
||||||
_records = null;
|
|
||||||
});
|
|
||||||
var requestCursor = 1;
|
|
||||||
final allRecords = <SCRoomRocketRewardRecordRes>[];
|
|
||||||
try {
|
|
||||||
while (mounted) {
|
|
||||||
_rocketUiDebug(
|
|
||||||
'request reward-records roomId=${widget.roomId} '
|
|
||||||
'cursor=$requestCursor limit=$_rocketPageSize',
|
|
||||||
);
|
|
||||||
final res = await SCChatRoomRepository().roomRocketRewardRecords(
|
|
||||||
widget.roomId,
|
|
||||||
cursor: requestCursor,
|
|
||||||
limit: _rocketPageSize,
|
|
||||||
);
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final nextRecords = res.records;
|
|
||||||
allRecords.addAll(nextRecords);
|
|
||||||
_rocketUiDebug(
|
|
||||||
'reward-records loaded cursor=$requestCursor '
|
|
||||||
'count=${nextRecords.length} total=${allRecords.length}',
|
|
||||||
);
|
|
||||||
if (nextRecords.length < _rocketPageSize) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
requestCursor += 1;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
_rocketUiDebug(
|
||||||
|
'request reward-records roomId=${widget.roomId} '
|
||||||
|
'cursor=$requestCursor limit=$_rocketPageSize',
|
||||||
|
);
|
||||||
|
final res = await SCChatRoomRepository().roomRocketRewardRecords(
|
||||||
|
widget.roomId,
|
||||||
|
cursor: requestCursor,
|
||||||
|
limit: _rocketPageSize,
|
||||||
|
);
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
final nextRecords = res.records;
|
||||||
setState(() {
|
setState(() {
|
||||||
_rawRecords
|
if (reset) {
|
||||||
..clear()
|
_rawRecords
|
||||||
..addAll(allRecords);
|
..clear()
|
||||||
|
..addAll(nextRecords);
|
||||||
|
} else {
|
||||||
|
_rawRecords.addAll(nextRecords);
|
||||||
|
}
|
||||||
_records = RoomRocketApiMapper.recordItemsFromRecords(_rawRecords);
|
_records = RoomRocketApiMapper.recordItemsFromRecords(_rawRecords);
|
||||||
_hasMore = false;
|
_hasMore = nextRecords.length >= _rocketPageSize;
|
||||||
_cursor = requestCursor;
|
if (_hasMore) {
|
||||||
|
_cursor = requestCursor + 1;
|
||||||
|
}
|
||||||
_loading = false;
|
_loading = false;
|
||||||
_loadingMore = false;
|
_loadingMore = false;
|
||||||
});
|
});
|
||||||
_rocketUiDebug(
|
_rocketUiDebug(
|
||||||
'reward-records all loaded total=${_rawRecords.length} '
|
'reward-records loaded cursor=$requestCursor '
|
||||||
'lastCursor=$_cursor',
|
'count=${nextRecords.length} total=${_rawRecords.length} '
|
||||||
|
'hasMore=$_hasMore nextCursor=$_cursor',
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
_rocketUiDebug('reward-records request failed: $error');
|
_rocketUiDebug('reward-records request failed: $error');
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (allRecords.isNotEmpty) {
|
|
||||||
_rawRecords
|
|
||||||
..clear()
|
|
||||||
..addAll(allRecords);
|
|
||||||
_records = RoomRocketApiMapper.recordItemsFromRecords(_rawRecords);
|
|
||||||
}
|
|
||||||
_loading = false;
|
_loading = false;
|
||||||
_loadingMore = false;
|
_loadingMore = false;
|
||||||
_hasMore = false;
|
_hasMore = false;
|
||||||
@ -999,7 +871,7 @@ class _RoomRocketRecordDialogLoaderState
|
|||||||
loadingText: _loading ? 'Loading data......' : '',
|
loadingText: _loading ? 'Loading data......' : '',
|
||||||
isLoadingMore: _loadingMore,
|
isLoadingMore: _loadingMore,
|
||||||
hasMore: _hasMore,
|
hasMore: _hasMore,
|
||||||
onLoadMore: null,
|
onLoadMore: () => unawaited(_load()),
|
||||||
limitText: 'Show only records from the last 35 days',
|
limitText: 'Show only records from the last 35 days',
|
||||||
onClose: widget.onClose,
|
onClose: widget.onClose,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -11,21 +11,17 @@ import 'package:yumi/ui_kit/components/custom_cached_image.dart';
|
|||||||
import 'package:yumi/ui_kit/components/sc_rotating_dots_loading.dart';
|
import 'package:yumi/ui_kit/components/sc_rotating_dots_loading.dart';
|
||||||
|
|
||||||
const Duration _pagMemoryTtl = Duration(minutes: 5);
|
const Duration _pagMemoryTtl = Duration(minutes: 5);
|
||||||
const Size _roomRocketFallbackPagRawSize = Size(750, 1624);
|
|
||||||
const Rect _roomRocketTopAvatarRawRect = Rect.fromLTWH(291, 164, 168, 168);
|
|
||||||
|
|
||||||
class RoomRocketPagEffectOverlay extends StatefulWidget {
|
class RoomRocketPagEffectOverlay extends StatefulWidget {
|
||||||
const RoomRocketPagEffectOverlay({
|
const RoomRocketPagEffectOverlay({
|
||||||
super.key,
|
super.key,
|
||||||
required this.resource,
|
required this.resource,
|
||||||
this.topAvatarUrl,
|
this.topAvatarUrl,
|
||||||
this.topAvatarUrlListenable,
|
|
||||||
this.onCompleted,
|
this.onCompleted,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String resource;
|
final String resource;
|
||||||
final String? topAvatarUrl;
|
final String? topAvatarUrl;
|
||||||
final ValueListenable<String?>? topAvatarUrlListenable;
|
|
||||||
final VoidCallback? onCompleted;
|
final VoidCallback? onCompleted;
|
||||||
|
|
||||||
static bool isPag(String? resource) {
|
static bool isPag(String? resource) {
|
||||||
@ -54,7 +50,6 @@ class RoomRocketPagEffectOverlay extends StatefulWidget {
|
|||||||
class _RoomRocketPagEffectOverlayState
|
class _RoomRocketPagEffectOverlayState
|
||||||
extends State<RoomRocketPagEffectOverlay> {
|
extends State<RoomRocketPagEffectOverlay> {
|
||||||
bool _completed = false;
|
bool _completed = false;
|
||||||
Size? _pagRawSize;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -70,7 +65,10 @@ class _RoomRocketPagEffectOverlayState
|
|||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
_buildTopAvatar(size),
|
_RoomRocketPagTopAvatar(
|
||||||
|
avatarUrl: widget.topAvatarUrl,
|
||||||
|
canvasSize: size,
|
||||||
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: _RoomRocketPagView(
|
child: _RoomRocketPagView(
|
||||||
resource: resource,
|
resource: resource,
|
||||||
@ -81,7 +79,6 @@ class _RoomRocketPagEffectOverlayState
|
|||||||
displayScale: 1,
|
displayScale: 1,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
memoryTtl: _pagMemoryTtl,
|
memoryTtl: _pagMemoryTtl,
|
||||||
onReady: _updatePagRawSize,
|
|
||||||
onCompleted: _complete,
|
onCompleted: _complete,
|
||||||
defaultBuilder:
|
defaultBuilder:
|
||||||
(_) => const Center(child: SCRotatingDotsLoading()),
|
(_) => const Center(child: SCRotatingDotsLoading()),
|
||||||
@ -102,48 +99,16 @@ class _RoomRocketPagEffectOverlayState
|
|||||||
_completed = true;
|
_completed = true;
|
||||||
widget.onCompleted?.call();
|
widget.onCompleted?.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updatePagRawSize(Size rawSize) {
|
|
||||||
if (_pagRawSize == rawSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() => _pagRawSize = rawSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildTopAvatar(Size canvasSize) {
|
|
||||||
final listenable = widget.topAvatarUrlListenable;
|
|
||||||
if (listenable == null) {
|
|
||||||
return _RoomRocketPagTopAvatar(
|
|
||||||
avatarUrl: widget.topAvatarUrl,
|
|
||||||
canvasSize: canvasSize,
|
|
||||||
pagRawSize: _pagRawSize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ValueListenableBuilder<String?>(
|
|
||||||
valueListenable: listenable,
|
|
||||||
builder:
|
|
||||||
(context, avatarUrl, _) => _RoomRocketPagTopAvatar(
|
|
||||||
avatarUrl: _firstNonBlankPagAvatar([
|
|
||||||
avatarUrl,
|
|
||||||
widget.topAvatarUrl,
|
|
||||||
]),
|
|
||||||
canvasSize: canvasSize,
|
|
||||||
pagRawSize: _pagRawSize,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoomRocketPagTopAvatar extends StatelessWidget {
|
class _RoomRocketPagTopAvatar extends StatelessWidget {
|
||||||
const _RoomRocketPagTopAvatar({
|
const _RoomRocketPagTopAvatar({
|
||||||
required this.avatarUrl,
|
required this.avatarUrl,
|
||||||
required this.canvasSize,
|
required this.canvasSize,
|
||||||
required this.pagRawSize,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? avatarUrl;
|
final String? avatarUrl;
|
||||||
final Size canvasSize;
|
final Size canvasSize;
|
||||||
final Size? pagRawSize;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -151,13 +116,12 @@ class _RoomRocketPagTopAvatar extends StatelessWidget {
|
|||||||
if (url.isEmpty) {
|
if (url.isEmpty) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
final rect = _mapRoomRocketPagRawRectToCoverCanvas(
|
final size = (canvasSize.width * 0.15).clamp(46.0, 66.0).toDouble();
|
||||||
rawRect: _roomRocketTopAvatarRawRect,
|
return Positioned(
|
||||||
rawSize: pagRawSize ?? _roomRocketFallbackPagRawSize,
|
left: (canvasSize.width - size) / 2,
|
||||||
canvasSize: canvasSize,
|
top: canvasSize.height * 0.098,
|
||||||
);
|
width: size,
|
||||||
return Positioned.fromRect(
|
height: size,
|
||||||
rect: rect,
|
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: CustomCachedImage(
|
child: CustomCachedImage(
|
||||||
imageUrl: url,
|
imageUrl: url,
|
||||||
@ -236,7 +200,6 @@ class _RoomRocketPagView extends StatefulWidget {
|
|||||||
required this.clipBehavior,
|
required this.clipBehavior,
|
||||||
required this.memoryTtl,
|
required this.memoryTtl,
|
||||||
this.underlayBuilder,
|
this.underlayBuilder,
|
||||||
this.onReady,
|
|
||||||
this.onCompleted,
|
this.onCompleted,
|
||||||
this.defaultBuilder,
|
this.defaultBuilder,
|
||||||
});
|
});
|
||||||
@ -250,7 +213,6 @@ class _RoomRocketPagView extends StatefulWidget {
|
|||||||
final Clip clipBehavior;
|
final Clip clipBehavior;
|
||||||
final Duration memoryTtl;
|
final Duration memoryTtl;
|
||||||
final WidgetBuilder? underlayBuilder;
|
final WidgetBuilder? underlayBuilder;
|
||||||
final ValueChanged<Size>? onReady;
|
|
||||||
final VoidCallback? onCompleted;
|
final VoidCallback? onCompleted;
|
||||||
final WidgetBuilder? defaultBuilder;
|
final WidgetBuilder? defaultBuilder;
|
||||||
|
|
||||||
@ -452,7 +414,6 @@ class _RoomRocketPagViewState extends State<_RoomRocketPagView> {
|
|||||||
'viewSize=${widget.width?.toStringAsFixed(1)}x'
|
'viewSize=${widget.width?.toStringAsFixed(1)}x'
|
||||||
'${widget.height?.toStringAsFixed(1)} fit=${widget.fit.name}',
|
'${widget.height?.toStringAsFixed(1)} fit=${widget.fit.name}',
|
||||||
);
|
);
|
||||||
widget.onReady?.call(Size(rawWidth, rawHeight));
|
|
||||||
unawaited(_debugLayers(state, rawWidth, rawHeight));
|
unawaited(_debugLayers(state, rawWidth, rawHeight));
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
setState(() => _initialized = true);
|
setState(() => _initialized = true);
|
||||||
@ -626,55 +587,6 @@ Timer? _schedulePagMemoryReleaseTimer(
|
|||||||
return Timer(memoryTtl, onRelease);
|
return Timer(memoryTtl, onRelease);
|
||||||
}
|
}
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
Rect debugMapRoomRocketPagRawRectToCoverCanvas({
|
|
||||||
required Rect rawRect,
|
|
||||||
required Size rawSize,
|
|
||||||
required Size canvasSize,
|
|
||||||
}) {
|
|
||||||
return _mapRoomRocketPagRawRectToCoverCanvas(
|
|
||||||
rawRect: rawRect,
|
|
||||||
rawSize: rawSize,
|
|
||||||
canvasSize: canvasSize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rect _mapRoomRocketPagRawRectToCoverCanvas({
|
|
||||||
required Rect rawRect,
|
|
||||||
required Size rawSize,
|
|
||||||
required Size canvasSize,
|
|
||||||
}) {
|
|
||||||
if (rawSize.width <= 0 ||
|
|
||||||
rawSize.height <= 0 ||
|
|
||||||
canvasSize.width <= 0 ||
|
|
||||||
canvasSize.height <= 0) {
|
|
||||||
return Rect.zero;
|
|
||||||
}
|
|
||||||
final scaleX = canvasSize.width / rawSize.width;
|
|
||||||
final scaleY = canvasSize.height / rawSize.height;
|
|
||||||
final scale = scaleX > scaleY ? scaleX : scaleY;
|
|
||||||
final fittedWidth = rawSize.width * scale;
|
|
||||||
final fittedHeight = rawSize.height * scale;
|
|
||||||
final offsetX = (canvasSize.width - fittedWidth) / 2;
|
|
||||||
final offsetY = (canvasSize.height - fittedHeight) / 2;
|
|
||||||
return Rect.fromLTWH(
|
|
||||||
offsetX + rawRect.left * scale,
|
|
||||||
offsetY + rawRect.top * scale,
|
|
||||||
rawRect.width * scale,
|
|
||||||
rawRect.height * scale,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _firstNonBlankPagAvatar(Iterable<String?> values) {
|
|
||||||
for (final value in values) {
|
|
||||||
final text = value?.trim() ?? '';
|
|
||||||
if (text.isNotEmpty) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
String debugNormalizeRoomRocketPagResource(String resource) {
|
String debugNormalizeRoomRocketPagResource(String resource) {
|
||||||
return _normalizePagResource(resource);
|
return _normalizePagResource(resource);
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class RoomRocketRewardDialog extends StatelessWidget {
|
|||||||
final emptyText = _translate(
|
final emptyText = _translate(
|
||||||
localizations,
|
localizations,
|
||||||
'roomRocketRewardEmptyText',
|
'roomRocketRewardEmptyText',
|
||||||
'No reward',
|
'No rewards earned. Better luck next time.',
|
||||||
);
|
);
|
||||||
final contentDirection = Directionality.of(context);
|
final contentDirection = Directionality.of(context);
|
||||||
final resolvedRewards = _normalizeRewards(
|
final resolvedRewards = _normalizeRewards(
|
||||||
@ -257,7 +257,7 @@ class _RewardDialogCanvas extends StatelessWidget {
|
|||||||
if (isEmpty)
|
if (isEmpty)
|
||||||
_buildEmptyState()
|
_buildEmptyState()
|
||||||
else ...[
|
else ...[
|
||||||
rewards.isEmpty ? _buildRewardEmptyState() : _buildRewardGrid(),
|
_buildRewardGrid(),
|
||||||
_buildWinnerList(),
|
_buildWinnerList(),
|
||||||
],
|
],
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
@ -380,6 +380,9 @@ class _RewardDialogCanvas extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRewardGrid() {
|
Widget _buildRewardGrid() {
|
||||||
|
if (rewards.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
final columns = math.max(3, (rewards.length / 2).ceil());
|
final columns = math.max(3, (rewards.length / 2).ceil());
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: s(28),
|
left: s(28),
|
||||||
@ -422,45 +425,6 @@ class _RewardDialogCanvas extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRewardEmptyState() {
|
|
||||||
return Positioned(
|
|
||||||
left: s(0),
|
|
||||||
top: s(268),
|
|
||||||
width: s(_designWidth),
|
|
||||||
height: s(178),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
_RewardDialogAssets.empty,
|
|
||||||
width: s(72),
|
|
||||||
height: s(72),
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
SizedBox(height: s(10)),
|
|
||||||
SizedBox(
|
|
||||||
width: s(250),
|
|
||||||
child: Text(
|
|
||||||
emptyText,
|
|
||||||
textDirection: contentDirection,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white.withValues(alpha: 0.72),
|
|
||||||
fontSize: s(13),
|
|
||||||
height: 1.25,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildWinnerList() {
|
Widget _buildWinnerList() {
|
||||||
return Positioned(
|
return Positioned(
|
||||||
left: s(12),
|
left: s(12),
|
||||||
|
|||||||
@ -81,7 +81,7 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///火箭用户中奖
|
///火箭用户中奖
|
||||||
if (SCRoomMsgType.isRocketRewardUserType(widget.msg.type)) {
|
if (widget.msg.type == SCRoomMsgType.rocketRewardUser) {
|
||||||
return _buildRocketRewardMsg(context);
|
return _buildRocketRewardMsg(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,17 +692,10 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
///火箭用户中奖
|
///火箭用户中奖
|
||||||
_buildRocketRewardMsg(BuildContext context) {
|
_buildRocketRewardMsg(BuildContext context) {
|
||||||
List<InlineSpan> spans = [];
|
List<InlineSpan> spans = [];
|
||||||
final nickname = Msg._firstNonBlankString([
|
|
||||||
widget.msg.user?.userNickname,
|
|
||||||
widget.msg.user?.account,
|
|
||||||
widget.msg.user?.id,
|
|
||||||
SCAppLocalizations.of(context)?.member,
|
|
||||||
'Member',
|
|
||||||
]);
|
|
||||||
|
|
||||||
spans.add(
|
spans.add(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: "$nickname ",
|
text: "${widget.msg.user?.userNickname} ",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: sp(12),
|
fontSize: sp(12),
|
||||||
color: SocialChatTheme.primaryColor,
|
color: SocialChatTheme.primaryColor,
|
||||||
@ -2002,118 +1995,6 @@ class Msg {
|
|||||||
|
|
||||||
gift =
|
gift =
|
||||||
json['gift'] != null ? SocialChatGiftRes.fromJson(json['gift']) : null;
|
json['gift'] != null ? SocialChatGiftRes.fromJson(json['gift']) : null;
|
||||||
_hydrateRocketRewardPayload(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _hydrateRocketRewardPayload(dynamic json) {
|
|
||||||
if (!SCRoomMsgType.isRocketRewardUserType(type)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final root = _stringKeyMap(json);
|
|
||||||
final payload = _stringKeyMap(root['data']);
|
|
||||||
final source = payload.isEmpty ? root : payload;
|
|
||||||
groupId = _firstNonBlankString([
|
|
||||||
groupId,
|
|
||||||
source['groupId'],
|
|
||||||
source['roomAccount'],
|
|
||||||
source['room_account'],
|
|
||||||
source['roomId'],
|
|
||||||
source['room_id'],
|
|
||||||
]);
|
|
||||||
role = _firstNonBlankString([
|
|
||||||
role,
|
|
||||||
source['rewardType'],
|
|
||||||
source['reward_type'],
|
|
||||||
]);
|
|
||||||
rocketIconUrl = _firstNonBlankString([
|
|
||||||
rocketIconUrl,
|
|
||||||
source['rocketIconUrl'],
|
|
||||||
source['rocket_icon_url'],
|
|
||||||
source['rocketIcon'],
|
|
||||||
source['rewardCover'],
|
|
||||||
source['reward_cover'],
|
|
||||||
]);
|
|
||||||
msg = _firstNonBlankString([
|
|
||||||
msg,
|
|
||||||
source['rewardCover'],
|
|
||||||
source['reward_cover'],
|
|
||||||
source['rewardIconUrl'],
|
|
||||||
source['reward_icon_url'],
|
|
||||||
source['cover'],
|
|
||||||
rocketIconUrl,
|
|
||||||
]);
|
|
||||||
user ??= _rocketRewardUserProfile(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SocialChatUserProfile? _rocketRewardUserProfile(
|
|
||||||
Map<String, dynamic> source,
|
|
||||||
) {
|
|
||||||
final userId = _firstNonBlankString([
|
|
||||||
source['userId'],
|
|
||||||
source['user_id'],
|
|
||||||
source['rewardUserId'],
|
|
||||||
source['reward_user_id'],
|
|
||||||
source['receiverUserId'],
|
|
||||||
source['receiver_user_id'],
|
|
||||||
source['targetUserId'],
|
|
||||||
source['target_user_id'],
|
|
||||||
]);
|
|
||||||
final nickname = _firstNonBlankString([
|
|
||||||
source['userNickname'],
|
|
||||||
source['user_nickname'],
|
|
||||||
source['nickname'],
|
|
||||||
source['receiverNickname'],
|
|
||||||
source['receiver_nickname'],
|
|
||||||
source['account'],
|
|
||||||
userId,
|
|
||||||
]);
|
|
||||||
final account = _firstNonBlankString([
|
|
||||||
source['account'],
|
|
||||||
source['userAccount'],
|
|
||||||
source['user_account'],
|
|
||||||
source['actualAccount'],
|
|
||||||
source['actual_account'],
|
|
||||||
userId,
|
|
||||||
]);
|
|
||||||
final avatar = _firstNonBlankString([
|
|
||||||
source['userAvatar'],
|
|
||||||
source['user_avatar'],
|
|
||||||
source['avatar'],
|
|
||||||
source['receiverAvatar'],
|
|
||||||
source['receiver_avatar'],
|
|
||||||
]);
|
|
||||||
if (userId.isEmpty &&
|
|
||||||
nickname.isEmpty &&
|
|
||||||
account.isEmpty &&
|
|
||||||
avatar.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return SocialChatUserProfile(
|
|
||||||
id: userId,
|
|
||||||
account: account,
|
|
||||||
userNickname: nickname,
|
|
||||||
userAvatar: avatar,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, dynamic> _stringKeyMap(dynamic value) {
|
|
||||||
if (value is Map<String, dynamic>) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (value is Map) {
|
|
||||||
return value.map((key, item) => MapEntry(key.toString(), item));
|
|
||||||
}
|
|
||||||
return const <String, dynamic>{};
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _firstNonBlankString(Iterable<dynamic> values) {
|
|
||||||
for (final value in values) {
|
|
||||||
final text = value?.toString().trim() ?? '';
|
|
||||||
if (text.isNotEmpty && text.toLowerCase() != 'null') {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|||||||
@ -49,10 +49,6 @@ class RoomUserInfoCard extends StatefulWidget {
|
|||||||
class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
||||||
static const String _leaveMicActionIconAsset =
|
static const String _leaveMicActionIconAsset =
|
||||||
"sc_images/room/sc_icon_room_user_card_leave_mic.png";
|
"sc_images/room/sc_icon_room_user_card_leave_mic.png";
|
||||||
static const String _defaultProfileCardBackgroundAsset =
|
|
||||||
"sc_images/room/sc_bg_room_user_profile_card_default.png";
|
|
||||||
static const double _profileSheetHeightFactor = 0.4;
|
|
||||||
static const double _dataCardBackgroundTranslateFactor = 0.8;
|
|
||||||
|
|
||||||
SocialChatUserProfileManager? userProvider;
|
SocialChatUserProfileManager? userProvider;
|
||||||
String roomId = "";
|
String roomId = "";
|
||||||
@ -89,8 +85,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenHeight = MediaQuery.of(context).size.height;
|
final sheetHeight = MediaQuery.of(context).size.height * 0.6;
|
||||||
final defaultSheetHeight = screenHeight * _profileSheetHeightFactor;
|
|
||||||
|
|
||||||
return Consumer<SocialChatUserProfileManager>(
|
return Consumer<SocialChatUserProfileManager>(
|
||||||
builder: (context, ref, child) {
|
builder: (context, ref, child) {
|
||||||
@ -105,18 +100,14 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
final canLeaveMic = currentMicIndex > -1;
|
final canLeaveMic = currentMicIndex > -1;
|
||||||
|
|
||||||
if (ref.userCardInfo == null) {
|
if (ref.userCardInfo == null) {
|
||||||
return _buildLoadingSheet(defaultSheetHeight);
|
return _buildLoadingSheet(sheetHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
final dataCard = _activeDataCard(ref.userCardInfo);
|
|
||||||
|
|
||||||
return _buildProfileSheet(
|
return _buildProfileSheet(
|
||||||
context: context,
|
context: context,
|
||||||
ref: ref,
|
ref: ref,
|
||||||
rtcProvider: rtcProvider,
|
rtcProvider: rtcProvider,
|
||||||
sheetHeight: defaultSheetHeight,
|
sheetHeight: sheetHeight,
|
||||||
dataCardBackgroundHeight: screenHeight,
|
|
||||||
dataCard: dataCard,
|
|
||||||
isSelf: isSelf,
|
isSelf: isSelf,
|
||||||
currentMicIndex: currentMicIndex,
|
currentMicIndex: currentMicIndex,
|
||||||
canLeaveMic: canLeaveMic,
|
canLeaveMic: canLeaveMic,
|
||||||
@ -132,21 +123,19 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
height: sheetHeight,
|
height: sheetHeight,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
||||||
child: Stack(
|
child: DecoratedBox(
|
||||||
children: [
|
decoration: _sheetDecoration(),
|
||||||
Positioned.fill(child: _buildDefaultSheetBackground()),
|
child: Center(
|
||||||
Center(
|
child: Container(
|
||||||
child: Container(
|
width: 55.w,
|
||||||
width: 55.w,
|
height: 55.w,
|
||||||
height: 55.w,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.black26,
|
||||||
color: Colors.black26,
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
|
||||||
),
|
|
||||||
child: CupertinoActivityIndicator(color: Colors.white24),
|
|
||||||
),
|
),
|
||||||
|
child: CupertinoActivityIndicator(color: Colors.white24),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -157,8 +146,6 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
required SocialChatUserProfileManager ref,
|
required SocialChatUserProfileManager ref,
|
||||||
required RtcProvider rtcProvider,
|
required RtcProvider rtcProvider,
|
||||||
required double sheetHeight,
|
required double sheetHeight,
|
||||||
required double dataCardBackgroundHeight,
|
|
||||||
required PropsResources? dataCard,
|
|
||||||
required bool isSelf,
|
required bool isSelf,
|
||||||
required num currentMicIndex,
|
required num currentMicIndex,
|
||||||
required bool canLeaveMic,
|
required bool canLeaveMic,
|
||||||
@ -166,215 +153,138 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
}) {
|
}) {
|
||||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||||
final profile = ref.userCardInfo?.userProfile;
|
final profile = ref.userCardInfo?.userProfile;
|
||||||
|
final dataCard = _activeDataCard(ref.userCardInfo);
|
||||||
final canShowReport = currentUserId != widget.userId;
|
final canShowReport = currentUserId != widget.userId;
|
||||||
final canShowSetting =
|
final canShowSetting =
|
||||||
canShowReport &&
|
canShowReport &&
|
||||||
ref.userCardInfo?.roomRole != SCRoomRolesType.HOMEOWNER.name &&
|
ref.userCardInfo?.roomRole != SCRoomRolesType.HOMEOWNER.name &&
|
||||||
(rtcProvider.isFz() || rtcProvider.isGL());
|
(rtcProvider.isFz() || rtcProvider.isGL());
|
||||||
|
|
||||||
final profileContent = _buildProfileContentLayer(
|
|
||||||
context: context,
|
|
||||||
ref: ref,
|
|
||||||
rtcProvider: rtcProvider,
|
|
||||||
profile: profile,
|
|
||||||
bottomPadding: bottomPadding,
|
|
||||||
isSelf: isSelf,
|
|
||||||
canLeaveMic: canLeaveMic,
|
|
||||||
currentMicIndex: currentMicIndex,
|
|
||||||
canShowReport: canShowReport,
|
|
||||||
canShowSetting: canShowSetting,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (dataCard == null) {
|
|
||||||
return SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: sheetHeight,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(child: _buildDefaultSheetBackground()),
|
|
||||||
Positioned.fill(child: profileContent),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _buildDataCardFrame(
|
|
||||||
sheetHeight: sheetHeight,
|
|
||||||
backgroundHeight: dataCardBackgroundHeight,
|
|
||||||
dataCard: dataCard,
|
|
||||||
child: profileContent,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDataCardFrame({
|
|
||||||
required double sheetHeight,
|
|
||||||
required double backgroundHeight,
|
|
||||||
required PropsResources dataCard,
|
|
||||||
required Widget child,
|
|
||||||
}) {
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: sheetHeight,
|
height: sheetHeight,
|
||||||
child: Stack(
|
child: ClipRRect(
|
||||||
clipBehavior: Clip.none,
|
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
||||||
children: [
|
child: Stack(
|
||||||
Positioned.fill(
|
children: [
|
||||||
child: ClipRRect(
|
Positioned.fill(child: _buildSheetBackground(dataCard)),
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
Positioned.fill(
|
||||||
child: DecoratedBox(decoration: _sheetDecoration()),
|
child: SingleChildScrollView(
|
||||||
|
physics: const ClampingScrollPhysics(),
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
18.w,
|
||||||
|
16.w,
|
||||||
|
18.w,
|
||||||
|
bottomPadding + 10.w,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
_buildProfileHeader(context, profile, ref),
|
||||||
|
SizedBox(height: 6.w),
|
||||||
|
_buildInfoChips(context, profile),
|
||||||
|
SizedBox(height: 9.w),
|
||||||
|
_buildBadgeStrip(
|
||||||
|
profile?.id ?? widget.userId,
|
||||||
|
ref,
|
||||||
|
profile,
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.w),
|
||||||
|
_buildActions(
|
||||||
|
context: context,
|
||||||
|
ref: ref,
|
||||||
|
rtcProvider: rtcProvider,
|
||||||
|
isSelf: isSelf,
|
||||||
|
canLeaveMic: canLeaveMic,
|
||||||
|
currentMicIndex: currentMicIndex,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
if (canShowReport)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
top: 14.w,
|
||||||
right: 0,
|
right: 16.w,
|
||||||
top:
|
child: _buildHeaderIconButton(
|
||||||
-(backgroundHeight - sheetHeight) +
|
iconAsset: "sc_images/room/sc_icon_user_card_report.png",
|
||||||
sheetHeight * _dataCardBackgroundTranslateFactor,
|
onTap: () {
|
||||||
height: backgroundHeight,
|
final route =
|
||||||
child: IgnorePointer(child: _buildSheetBackground(dataCard)),
|
"${SCMainRoute.report}?type=user&tageId=${widget.userId}";
|
||||||
),
|
Navigator.of(context).pop();
|
||||||
Positioned.fill(
|
SCNavigatorUtils.push(
|
||||||
child: ClipRRect(
|
navigatorKey.currentState?.context ?? context,
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(18.w)),
|
route,
|
||||||
child: child,
|
replace: false,
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
if (canShowSetting)
|
||||||
|
Positioned(
|
||||||
|
top: 14.w,
|
||||||
|
right: 52.w,
|
||||||
|
child: _buildHeaderIconButton(
|
||||||
|
iconAsset:
|
||||||
|
"sc_images/room/sc_icon_room_user_card_setting.png",
|
||||||
|
onTap: () {
|
||||||
|
if (userProvider?.userCardInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
showBottomInBottomDialog(
|
||||||
|
navigatorKey.currentState?.context ?? context,
|
||||||
|
RoomUserCardSetting(
|
||||||
|
roomId: roomId,
|
||||||
|
userCardInfo: userProvider?.userCardInfo?.copyWith(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProfileContentLayer({
|
|
||||||
required BuildContext context,
|
|
||||||
required SocialChatUserProfileManager ref,
|
|
||||||
required RtcProvider rtcProvider,
|
|
||||||
required SocialChatUserProfile? profile,
|
|
||||||
required double bottomPadding,
|
|
||||||
required bool isSelf,
|
|
||||||
required bool canLeaveMic,
|
|
||||||
required num currentMicIndex,
|
|
||||||
required bool canShowReport,
|
|
||||||
required bool canShowSetting,
|
|
||||||
}) {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
return Align(
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: FittedBox(
|
|
||||||
fit: BoxFit.scaleDown,
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: SizedBox(
|
|
||||||
width: constraints.maxWidth,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(
|
|
||||||
18.w,
|
|
||||||
10.w,
|
|
||||||
18.w,
|
|
||||||
bottomPadding > 6.w ? bottomPadding : 6.w,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
_buildProfileHeader(context, profile, ref),
|
|
||||||
SizedBox(height: 6.w),
|
|
||||||
_buildBadgeStrip(
|
|
||||||
profile?.id ?? widget.userId,
|
|
||||||
ref,
|
|
||||||
profile,
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
_buildActions(
|
|
||||||
context: context,
|
|
||||||
ref: ref,
|
|
||||||
rtcProvider: rtcProvider,
|
|
||||||
isSelf: isSelf,
|
|
||||||
canLeaveMic: canLeaveMic,
|
|
||||||
currentMicIndex: currentMicIndex,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (canShowReport)
|
|
||||||
Positioned(
|
|
||||||
top: 14.w,
|
|
||||||
right: 16.w,
|
|
||||||
child: _buildHeaderIconButton(
|
|
||||||
iconAsset: "sc_images/room/sc_icon_user_card_report.png",
|
|
||||||
onTap: () {
|
|
||||||
final route =
|
|
||||||
"${SCMainRoute.report}?type=user&tageId=${widget.userId}";
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
SCNavigatorUtils.push(
|
|
||||||
navigatorKey.currentState?.context ?? context,
|
|
||||||
route,
|
|
||||||
replace: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (canShowSetting)
|
|
||||||
Positioned(
|
|
||||||
top: 14.w,
|
|
||||||
right: 52.w,
|
|
||||||
child: _buildHeaderIconButton(
|
|
||||||
iconAsset: "sc_images/room/sc_icon_room_user_card_setting.png",
|
|
||||||
onTap: () {
|
|
||||||
if (userProvider?.userCardInfo == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
showBottomInBottomDialog(
|
|
||||||
navigatorKey.currentState?.context ?? context,
|
|
||||||
RoomUserCardSetting(
|
|
||||||
roomId: roomId,
|
|
||||||
userCardInfo: userProvider?.userCardInfo?.copyWith(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
BoxDecoration _sheetDecoration() {
|
BoxDecoration _sheetDecoration() {
|
||||||
return const BoxDecoration(color: Color(0xff061F1E));
|
return const BoxDecoration(
|
||||||
}
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
Widget _buildDefaultSheetBackground() {
|
end: Alignment.bottomCenter,
|
||||||
return DecoratedBox(
|
colors: [
|
||||||
decoration: _sheetDecoration(),
|
Color(0xff18F2B1),
|
||||||
child: SizedBox.expand(
|
Color(0xffE4FFF6),
|
||||||
child: Image.asset(
|
Color(0xffF7FFFC),
|
||||||
_defaultProfileCardBackgroundAsset,
|
Color(0xffFFFFFF),
|
||||||
fit: BoxFit.cover,
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSheetBackground(PropsResources? dataCard) {
|
Widget _buildSheetBackground(PropsResources? dataCard) {
|
||||||
if (dataCard == null) {
|
if (dataCard == null) {
|
||||||
return _buildDefaultSheetBackground();
|
return DecoratedBox(decoration: _sheetDecoration());
|
||||||
}
|
}
|
||||||
|
return Stack(
|
||||||
return SCDataCardResourceView(
|
fit: StackFit.expand,
|
||||||
resource: dataCard,
|
children: [
|
||||||
fit: BoxFit.cover,
|
SCDataCardResourceView(resource: dataCard, fit: BoxFit.cover),
|
||||||
showCoverFallback: false,
|
DecoratedBox(
|
||||||
allowCoverAsResource: false,
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.white.withValues(alpha: 0.08),
|
||||||
|
Colors.white.withValues(alpha: 0.22),
|
||||||
|
Colors.white.withValues(alpha: 0.58),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,8 +325,8 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
onTap: () => _openProfile(context),
|
onTap: () => _openProfile(context),
|
||||||
child: head(
|
child: head(
|
||||||
url: profile?.userAvatar ?? "",
|
url: profile?.userAvatar ?? "",
|
||||||
width: 105.w,
|
width: 70.w,
|
||||||
border: Border.all(color: Colors.white, width: 3),
|
border: Border.all(color: Colors.white, width: 2),
|
||||||
headdress: profile?.getHeaddress()?.sourceUrl,
|
headdress: profile?.getHeaddress()?.sourceUrl,
|
||||||
headdressCover: profile?.getHeaddress()?.cover,
|
headdressCover: profile?.getHeaddress()?.cover,
|
||||||
),
|
),
|
||||||
@ -437,7 +347,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
maxWidth: 116.w,
|
maxWidth: 116.w,
|
||||||
profile?.userNickname ?? "",
|
profile?.userNickname ?? "",
|
||||||
fontSize: 18.sp,
|
fontSize: 18.sp,
|
||||||
textColor: Colors.white,
|
textColor: Colors.black,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
type: "",
|
type: "",
|
||||||
needScroll: (profile?.userNickname?.characters.length ?? 0) > 12,
|
needScroll: (profile?.userNickname?.characters.length ?? 0) > 12,
|
||||||
@ -471,22 +381,6 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 2.w),
|
SizedBox(height: 2.w),
|
||||||
_buildSpecialIdMetaRow(context, profile),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSpecialIdMetaRow(
|
|
||||||
BuildContext context,
|
|
||||||
SocialChatUserProfile? profile,
|
|
||||||
) {
|
|
||||||
final flagUrl = _countryFlagUrl(context, profile);
|
|
||||||
final hasSex = profile?.userSex != null;
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
SCSpecialIdBadge(
|
SCSpecialIdBadge(
|
||||||
idText: profile?.getID() ?? "",
|
idText: profile?.getID() ?? "",
|
||||||
showAnimated: profile?.hasSpecialId() ?? false,
|
showAnimated: profile?.hasSpecialId() ?? false,
|
||||||
@ -498,62 +392,106 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
showAnimatedGradientText:
|
showAnimatedGradientText:
|
||||||
profile?.shouldShowColoredSpecialIdText() ?? false,
|
profile?.shouldShowColoredSpecialIdText() ?? false,
|
||||||
animationTextStyle: TextStyle(
|
animationTextStyle: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
normalTextStyle: TextStyle(
|
normalTextStyle: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.black,
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
animationFit: BoxFit.contain,
|
animationFit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
if (flagUrl.isNotEmpty) ...[
|
|
||||||
SizedBox(width: 8.w),
|
|
||||||
_buildCountryFlag(flagUrl),
|
|
||||||
],
|
|
||||||
if (hasSex) ...[SizedBox(width: 7.w), _buildSexIcon(profile)],
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _countryFlagUrl(BuildContext context, SocialChatUserProfile? profile) {
|
Widget _buildInfoChips(BuildContext context, SocialChatUserProfile? profile) {
|
||||||
final countryName = profile?.countryName ?? "";
|
return Row(
|
||||||
try {
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
return Provider.of<SCAppGeneralManager>(
|
children: [
|
||||||
context,
|
_buildCountryChip(context, profile),
|
||||||
listen: false,
|
SizedBox(width: 7.w),
|
||||||
).findCountryByName(countryName)?.nationalFlag ??
|
_buildSexAgeChip(profile),
|
||||||
"";
|
],
|
||||||
} catch (_) {
|
);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCountryFlag(String flagUrl) {
|
Widget _buildCountryChip(
|
||||||
final trimmedUrl = flagUrl.trim();
|
BuildContext context,
|
||||||
if (trimmedUrl.isEmpty) {
|
SocialChatUserProfile? profile,
|
||||||
return const SizedBox.shrink();
|
) {
|
||||||
}
|
final countryName = profile?.countryName ?? "";
|
||||||
|
final flagUrl =
|
||||||
|
Provider.of<SCAppGeneralManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).findCountryByName(countryName)?.nationalFlag ??
|
||||||
|
"";
|
||||||
|
|
||||||
return ClipRRect(
|
return Container(
|
||||||
borderRadius: BorderRadius.circular(2.w),
|
constraints: BoxConstraints(maxWidth: 145.w),
|
||||||
child: netImage(
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w),
|
||||||
url: trimmedUrl,
|
decoration: BoxDecoration(
|
||||||
width: 22.w,
|
color: Colors.white.withValues(alpha: 0.55),
|
||||||
height: 15.w,
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
fit: BoxFit.cover,
|
border: Border.all(
|
||||||
noDefaultImg: true,
|
color: const Color(0xff18F2B1).withValues(alpha: 0.18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
netImage(
|
||||||
|
url: flagUrl,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(3.w)),
|
||||||
|
width: 19.w,
|
||||||
|
height: 14.w,
|
||||||
|
),
|
||||||
|
SizedBox(width: 5.w),
|
||||||
|
Flexible(
|
||||||
|
child: text(
|
||||||
|
countryName,
|
||||||
|
maxLines: 1,
|
||||||
|
textColor: Colors.black,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSexIcon(SocialChatUserProfile? profile) {
|
Widget _buildSexAgeChip(SocialChatUserProfile? profile) {
|
||||||
if (profile?.userSex == null) {
|
return Container(
|
||||||
return const SizedBox.shrink();
|
height: 26.w,
|
||||||
}
|
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||||
return xb(profile?.userSex, height: 15.w, color: null);
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage(
|
||||||
|
profile?.userSex == 0
|
||||||
|
? "sc_images/login/sc_icon_sex_woman_bg.png"
|
||||||
|
: "sc_images/login/sc_icon_sex_man_bg.png",
|
||||||
|
),
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
xb(profile?.userSex),
|
||||||
|
SizedBox(width: 3.w),
|
||||||
|
text(
|
||||||
|
"${profile?.age ?? 0}",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeaderLongBadgeStrip(
|
Widget _buildHeaderLongBadgeStrip(
|
||||||
@ -609,7 +547,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
return wearBadges;
|
return wearBadges;
|
||||||
}
|
}
|
||||||
return ref.userCardInfo?.useBadge
|
return ref.userCardInfo?.useBadge
|
||||||
?.map<WearBadge>(_roomUseBadgeToWearBadge)
|
?.map(_roomUseBadgeToWearBadge)
|
||||||
.where(_hasBadgeUrl)
|
.where(_hasBadgeUrl)
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const <WearBadge>[];
|
const <WearBadge>[];
|
||||||
@ -724,7 +662,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
label,
|
label,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
textColor: Colors.white,
|
textColor: const Color(0xff666666),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
lineHeight: 1.1,
|
lineHeight: 1.1,
|
||||||
@ -779,7 +717,7 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
: SCAppLocalizations.of(context)!.follow,
|
: SCAppLocalizations.of(context)!.follow,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
textColor: Colors.white,
|
textColor: const Color(0xff666666),
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
lineHeight: 1.1,
|
lineHeight: 1.1,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
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_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -68,6 +70,11 @@ class _PropsStoreChatboxDetailDialogState
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
|
text(
|
||||||
|
widget.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"sc_images/general/sc_icon_jb.png",
|
"sc_images/general/sc_icon_jb.png",
|
||||||
|
|||||||
@ -74,7 +74,14 @@ class _PropsStoreDataCardDetailDialogState
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
const Spacer(),
|
Expanded(
|
||||||
|
child: text(
|
||||||
|
widget.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"sc_images/general/sc_icon_jb.png",
|
"sc_images/general/sc_icon_jb.png",
|
||||||
width: 22.w,
|
width: 22.w,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
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:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -76,6 +77,11 @@ class _PropsStoreHeaddressDetailDialogState
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
|
text(
|
||||||
|
widget.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"sc_images/general/sc_icon_jb.png",
|
"sc_images/general/sc_icon_jb.png",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
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:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -15,6 +16,7 @@ import '../../../app/constants/sc_global_config.dart';
|
|||||||
import '../../../app/routes/sc_fluro_navigator.dart';
|
import '../../../app/routes/sc_fluro_navigator.dart';
|
||||||
import '../../../shared/data_sources/models/enum/sc_currency_type.dart';
|
import '../../../shared/data_sources/models/enum/sc_currency_type.dart';
|
||||||
import '../../../shared/data_sources/models/enum/sc_props_type.dart';
|
import '../../../shared/data_sources/models/enum/sc_props_type.dart';
|
||||||
|
import '../../../shared/data_sources/sources/local/user_manager.dart';
|
||||||
import '../../../modules/wallet/wallet_route.dart';
|
import '../../../modules/wallet/wallet_route.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
import '../../components/sc_debounce_widget.dart';
|
import '../../components/sc_debounce_widget.dart';
|
||||||
@ -74,6 +76,11 @@ class _PropsStoreMountainsDetailDialogState
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
|
text(
|
||||||
|
widget.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"sc_images/general/sc_icon_jb.png",
|
"sc_images/general/sc_icon_jb.png",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
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:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -75,6 +76,11 @@ class _PropsStoreThemeDetailDialogState
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
|
text(
|
||||||
|
widget.res.propsResources?.name ?? "",
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
"sc_images/general/sc_icon_jb.png",
|
"sc_images/general/sc_icon_jb.png",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 7.1 KiB |
@ -1,45 +0,0 @@
|
|||||||
import 'package:flutter_test/flutter_test.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_dialog.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_floating_entry.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
test('rocket dialog title displays current level before daily round', () {
|
|
||||||
expect(debugRoomRocketTitleText(level: 3, round: 1), 'Lv.3 Today');
|
|
||||||
expect(
|
|
||||||
debugRoomRocketTitleText(level: 2, round: 4),
|
|
||||||
'Lv.2 · Round 4 Today',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(
|
|
||||||
'rocket level rail follows current level unless user selected manually',
|
|
||||||
() {
|
|
||||||
final status = SCRoomRocketStatusRes.fromJson({'currentLevel': 4});
|
|
||||||
const levels = [
|
|
||||||
RoomRocketLevelItem(label: 'Lv.2', level: 2),
|
|
||||||
RoomRocketLevelItem(label: 'Lv.4', level: 4),
|
|
||||||
RoomRocketLevelItem(label: 'Lv.5', level: 5),
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(
|
|
||||||
debugResolveRoomRocketSelectedLevelIndex(
|
|
||||||
levelItems: levels,
|
|
||||||
status: status,
|
|
||||||
selectedLevel: 0,
|
|
||||||
levelTouched: false,
|
|
||||||
),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
debugResolveRoomRocketSelectedLevelIndex(
|
|
||||||
levelItems: levels,
|
|
||||||
status: status,
|
|
||||||
selectedLevel: 2,
|
|
||||||
levelTouched: true,
|
|
||||||
),
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
test('hydrates rocket reward user from nested im payload', () {
|
|
||||||
final msg = Msg.fromJson({
|
|
||||||
'type': SCRoomMsgType.voiceRoomRocketRewardUser,
|
|
||||||
'data': {
|
|
||||||
'roomId': '9001',
|
|
||||||
'userId': '1001',
|
|
||||||
'userNickname': 'Winner',
|
|
||||||
'account': '223456',
|
|
||||||
'userAvatar': 'https://cdn.example.com/avatar.png',
|
|
||||||
'rewardType': 'GOLD',
|
|
||||||
'rewardCover': 'https://cdn.example.com/reward.png',
|
|
||||||
'rocketIconUrl': 'https://cdn.example.com/rocket.png',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(msg.type, SCRoomMsgType.voiceRoomRocketRewardUser);
|
|
||||||
expect(msg.groupId, '9001');
|
|
||||||
expect(msg.user?.id, '1001');
|
|
||||||
expect(msg.user?.userNickname, 'Winner');
|
|
||||||
expect(msg.user?.account, '223456');
|
|
||||||
expect(msg.user?.userAvatar, 'https://cdn.example.com/avatar.png');
|
|
||||||
expect(msg.role, 'GOLD');
|
|
||||||
expect(msg.msg, 'https://cdn.example.com/reward.png');
|
|
||||||
expect(msg.rocketIconUrl, 'https://cdn.example.com/rocket.png');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('rocket reward user nickname falls back to account', () {
|
|
||||||
final msg = Msg.fromJson({
|
|
||||||
'type': SCRoomMsgType.voiceRoomRocketRewardUser,
|
|
||||||
'userId': '1002',
|
|
||||||
'account': '334455',
|
|
||||||
'rewardType': 'BADGE',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(msg.user?.userNickname, '334455');
|
|
||||||
expect(msg.user?.id, '1002');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:yumi/app/config/app_config.dart';
|
import 'package:yumi/app/config/app_config.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
import 'package:yumi/app/constants/sc_global_config.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';
|
||||||
@ -61,19 +60,6 @@ void main() {
|
|||||||
timer?.cancel();
|
timer?.cancel();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('maps top avatar frame from pag raw canvas using cover fit', () {
|
|
||||||
final rect = debugMapRoomRocketPagRawRectToCoverCanvas(
|
|
||||||
rawRect: const Rect.fromLTWH(291, 164, 168, 168),
|
|
||||||
rawSize: const Size(750, 1624),
|
|
||||||
canvasSize: const Size(392.7272727, 829.090909),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(rect.left, closeTo(152.38, 0.01));
|
|
||||||
expect(rect.top, closeTo(75.23, 0.01));
|
|
||||||
expect(rect.width, closeTo(87.97, 0.01));
|
|
||||||
expect(rect.height, closeTo(87.97, 0.01));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('extracts launch pag urls from nested resource payloads', () {
|
test('extracts launch pag urls from nested resource payloads', () {
|
||||||
final launch = RoomRocketLaunchBroadcastMessage.fromJson({
|
final launch = RoomRocketLaunchBroadcastMessage.fromJson({
|
||||||
'rocketAnimationResource': {
|
'rocketAnimationResource': {
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_api_res.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_dialog.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_reward_dialog_loader.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -30,30 +27,6 @@ void main() {
|
|||||||
expect(records.map((item) => item.rewardAmount), [5000, 5200]);
|
expect(records.map((item) => item.rewardAmount), [5000, 5200]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('room rocket reward dialog', () {
|
|
||||||
testWidgets('shows reward empty state when room winners exist', (
|
|
||||||
tester,
|
|
||||||
) async {
|
|
||||||
await tester.pumpWidget(
|
|
||||||
const MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: RoomRocketRewardDialog(
|
|
||||||
rewards: <RoomRocketRewardItem>[],
|
|
||||||
winners: <RoomRocketRewardWinner>[
|
|
||||||
RoomRocketRewardWinner(nickname: 'test man'),
|
|
||||||
],
|
|
||||||
loadingText: '',
|
|
||||||
useMockFallback: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(find.text('No reward'), findsOneWidget);
|
|
||||||
expect(find.text('test man'), findsOneWidget);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SCRoomRocketRewardRecordRes _rewardRecord({
|
SCRoomRocketRewardRecordRes _rewardRecord({
|
||||||
|
|||||||
@ -122,37 +122,6 @@ void main() {
|
|||||||
expect(levels?.last.progressImageUrl, 'progress-2');
|
expect(levels?.last.progressImageUrl, 'progress-2');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parses disabled rocket status from go api', () {
|
|
||||||
final status = SCRoomRocketStatusRes.fromJson({
|
|
||||||
'configured': true,
|
|
||||||
'enabled': false,
|
|
||||||
'status': 'DISABLED',
|
|
||||||
'roomId': 'room-1',
|
|
||||||
'levels': [],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status.configured, isTrue);
|
|
||||||
expect(status.enabled, isFalse);
|
|
||||||
expect(status.isEnabled, isFalse);
|
|
||||||
expect(status.status, 'DISABLED');
|
|
||||||
expect(RoomRocketApiMapper.levelsFromStatus(status), isNull);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('treats unconfigured or disabled-status rocket as unavailable', () {
|
|
||||||
final unconfigured = SCRoomRocketStatusRes.fromJson({
|
|
||||||
'configured': false,
|
|
||||||
'enabled': true,
|
|
||||||
'status': 'ENABLED',
|
|
||||||
});
|
|
||||||
final disabledStatus = SCRoomRocketStatusRes.fromJson({
|
|
||||||
'configured': true,
|
|
||||||
'status': 'DISABLED',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(unconfigured.isEnabled, isFalse);
|
|
||||||
expect(disabledStatus.isEnabled, isFalse);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'uses top-level reward preview for the response level instead of current level',
|
'uses top-level reward preview for the response level instead of current level',
|
||||||
() {
|
() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user