启动页
@ -82,9 +82,10 @@
|
|||||||
"kingQuuen": "الملك والملكة",
|
"kingQuuen": "الملك والملكة",
|
||||||
"ramadan": "رمضان",
|
"ramadan": "رمضان",
|
||||||
"like": "إعجاب",
|
"like": "إعجاب",
|
||||||
"updateNow": "تحديث الآن",
|
"updateNow": "تحديث الآن",
|
||||||
"skip2": "تخطي",
|
"skip2": "تخطي",
|
||||||
"importantReminder": "تذكير مهم",
|
"skipCountdown": "تخطي {1}ث",
|
||||||
|
"importantReminder": "تذكير مهم",
|
||||||
"discard": "تجاهل",
|
"discard": "تجاهل",
|
||||||
"deleteCommentTips": "هل أنت متأكد أنك تريد حذف هذا التعليق؟",
|
"deleteCommentTips": "هل أنت متأكد أنك تريد حذف هذا التعليق؟",
|
||||||
"deleteSuccessful": "تم الحذف بنجاح!",
|
"deleteSuccessful": "تم الحذف بنجاح!",
|
||||||
|
|||||||
@ -96,6 +96,7 @@
|
|||||||
"ownerIncomeCoins": "মালিকের আয়:{1} কয়েন",
|
"ownerIncomeCoins": "মালিকের আয়:{1} কয়েন",
|
||||||
"game": "গেম",
|
"game": "গেম",
|
||||||
"skip2": "স্কিপ করুন",
|
"skip2": "স্কিপ করুন",
|
||||||
|
"skipCountdown": "{1}সে স্কিপ",
|
||||||
"coins4": "কয়েন",
|
"coins4": "কয়েন",
|
||||||
"claim": "গ্রহণ",
|
"claim": "গ্রহণ",
|
||||||
"complete": "সম্পূর্ণ",
|
"complete": "সম্পূর্ণ",
|
||||||
|
|||||||
@ -97,6 +97,7 @@
|
|||||||
"ownerIncomeCoins": "Owner's income: {1} coins",
|
"ownerIncomeCoins": "Owner's income: {1} coins",
|
||||||
"game": "Game",
|
"game": "Game",
|
||||||
"skip2": "Skip",
|
"skip2": "Skip",
|
||||||
|
"skipCountdown": "Skip {1}s",
|
||||||
"coins4": "Coins",
|
"coins4": "Coins",
|
||||||
"weekStart": "Week-Start",
|
"weekStart": "Week-Start",
|
||||||
"forMoreRewardsPleaseCheckTheTaskCenter": "For more rewards, please check the task center",
|
"forMoreRewardsPleaseCheckTheTaskCenter": "For more rewards, please check the task center",
|
||||||
|
|||||||
@ -85,9 +85,10 @@
|
|||||||
"applicationRecord": "Başvuru Kaydı",
|
"applicationRecord": "Başvuru Kaydı",
|
||||||
"appUpdateTip": "Uygulamanın yeni bir sürümü ({1}) var, şimdi indirmek ister misiniz?",
|
"appUpdateTip": "Uygulamanın yeni bir sürümü ({1}) var, şimdi indirmek ister misiniz?",
|
||||||
"ownerIncomeCoins": "Sahibin Geliri:{1} jetton",
|
"ownerIncomeCoins": "Sahibin Geliri:{1} jetton",
|
||||||
"game": "Oyun",
|
"game": "Oyun",
|
||||||
"skip2": "Atla",
|
"skip2": "Atla",
|
||||||
"coins4": "Jetton",
|
"skipCountdown": "{1} sn geç",
|
||||||
|
"coins4": "Jetton",
|
||||||
"weekStart": "Hafta Başlangıcı",
|
"weekStart": "Hafta Başlangıcı",
|
||||||
"forMoreRewardsPleaseCheckTheTaskCenter": "Daha fazla ödül için lütfen görev merkezini kontrol edin",
|
"forMoreRewardsPleaseCheckTheTaskCenter": "Daha fazla ödül için lütfen görev merkezini kontrol edin",
|
||||||
"kingQuuen": "Kral-Kraliçe",
|
"kingQuuen": "Kral-Kraliçe",
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class SCAppLocalizations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const LocalizationsDelegate<SCAppLocalizations> delegate =
|
static const LocalizationsDelegate<SCAppLocalizations> delegate =
|
||||||
_SCAppLocalizationsDelegate();
|
_SCAppLocalizationsDelegate();
|
||||||
|
|
||||||
Map<String, String>? _localizedStrings;
|
Map<String, String>? _localizedStrings;
|
||||||
|
|
||||||
@ -68,7 +68,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get expirationTime => translate('expirationTime');
|
String get expirationTime => translate('expirationTime');
|
||||||
|
|
||||||
String get inviteNewUsersToEarnCoins => translate('inviteNewUsersToEarnCoins');
|
String get inviteNewUsersToEarnCoins =>
|
||||||
|
translate('inviteNewUsersToEarnCoins');
|
||||||
|
|
||||||
String get avoidBeingKicked => translate('avoidBeingKicked');
|
String get avoidBeingKicked => translate('avoidBeingKicked');
|
||||||
|
|
||||||
@ -90,7 +91,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get numberOfMic => translate('numberOfMic');
|
String get numberOfMic => translate('numberOfMic');
|
||||||
|
|
||||||
String get noHistoricalRecordsAvailable => translate('noHistoricalRecordsAvailable');
|
String get noHistoricalRecordsAvailable =>
|
||||||
|
translate('noHistoricalRecordsAvailable');
|
||||||
|
|
||||||
String get myRoom => translate('myRoom');
|
String get myRoom => translate('myRoom');
|
||||||
|
|
||||||
@ -106,7 +108,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get crateMyRoom => translate('crateMyRoom');
|
String get crateMyRoom => translate('crateMyRoom');
|
||||||
|
|
||||||
String get exclusiveEmojiWillBeReleasedAfterBecoming => translate('exclusiveEmojiWillBeReleasedAfterBecoming');
|
String get exclusiveEmojiWillBeReleasedAfterBecoming =>
|
||||||
|
translate('exclusiveEmojiWillBeReleasedAfterBecoming');
|
||||||
|
|
||||||
String get socialPrivilege => translate('socialPrivilege');
|
String get socialPrivilege => translate('socialPrivilege');
|
||||||
|
|
||||||
@ -126,7 +129,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get vipBirthdayGift => translate('vipBirthdayGift');
|
String get vipBirthdayGift => translate('vipBirthdayGift');
|
||||||
|
|
||||||
String get pleaseUpgradeYourVipLevel => translate('pleaseUpgradeYourVipLevel');
|
String get pleaseUpgradeYourVipLevel =>
|
||||||
|
translate('pleaseUpgradeYourVipLevel');
|
||||||
|
|
||||||
String get membershipFreeChatSpeak => translate('membershipFreeChatSpeak');
|
String get membershipFreeChatSpeak => translate('membershipFreeChatSpeak');
|
||||||
|
|
||||||
@ -148,25 +152,34 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get enterTheRoom => translate('enterTheRoom');
|
String get enterTheRoom => translate('enterTheRoom');
|
||||||
|
|
||||||
String get taskNamePersonalGameConsume => translate('taskNamePersonalGameConsume');
|
String get taskNamePersonalGameConsume =>
|
||||||
|
translate('taskNamePersonalGameConsume');
|
||||||
|
|
||||||
String get taskNamePersonalMicInRoom => translate('taskNamePersonalMicInRoom');
|
String get taskNamePersonalMicInRoom =>
|
||||||
|
translate('taskNamePersonalMicInRoom');
|
||||||
|
|
||||||
String get taskNamePersonalActiveInRoom => translate('taskNamePersonalActiveInRoom');
|
String get taskNamePersonalActiveInRoom =>
|
||||||
|
translate('taskNamePersonalActiveInRoom');
|
||||||
|
|
||||||
String get taskNameRoomOwnerMicTime => translate('taskNameRoomOwnerMicTime');
|
String get taskNameRoomOwnerMicTime => translate('taskNameRoomOwnerMicTime');
|
||||||
|
|
||||||
String get taskNamePersonalLuckyGiftGold => translate('taskNamePersonalLuckyGiftGold');
|
String get taskNamePersonalLuckyGiftGold =>
|
||||||
|
translate('taskNamePersonalLuckyGiftGold');
|
||||||
|
|
||||||
String get taskNamePersonalMagicGiftGold => translate('taskNamePersonalMagicGiftGold');
|
String get taskNamePersonalMagicGiftGold =>
|
||||||
|
translate('taskNamePersonalMagicGiftGold');
|
||||||
|
|
||||||
String get taskNameRoomOwnerSendGiftGold => translate('taskNameRoomOwnerSendGiftGold');
|
String get taskNameRoomOwnerSendGiftGold =>
|
||||||
|
translate('taskNameRoomOwnerSendGiftGold');
|
||||||
|
|
||||||
String get taskNameRoomUserSendGiftGold => translate('taskNameRoomUserSendGiftGold');
|
String get taskNameRoomUserSendGiftGold =>
|
||||||
|
translate('taskNameRoomUserSendGiftGold');
|
||||||
|
|
||||||
String get taskNameRoomOwnerInviteMic => translate('taskNameRoomOwnerInviteMic');
|
String get taskNameRoomOwnerInviteMic =>
|
||||||
|
translate('taskNameRoomOwnerInviteMic');
|
||||||
|
|
||||||
String get taskNameRoomOnlineUserCount => translate('taskNameRoomOnlineUserCount');
|
String get taskNameRoomOnlineUserCount =>
|
||||||
|
translate('taskNameRoomOnlineUserCount');
|
||||||
|
|
||||||
String get taskNamePersonalSendGift => translate('taskNamePersonalSendGift');
|
String get taskNamePersonalSendGift => translate('taskNamePersonalSendGift');
|
||||||
|
|
||||||
@ -174,11 +187,14 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get taskNameRoomMicUser60Min => translate('taskNameRoomMicUser60Min');
|
String get taskNameRoomMicUser60Min => translate('taskNameRoomMicUser60Min');
|
||||||
|
|
||||||
String get taskNameRoomMicUser120Min => translate('taskNameRoomMicUser120Min');
|
String get taskNameRoomMicUser120Min =>
|
||||||
|
translate('taskNameRoomMicUser120Min');
|
||||||
|
|
||||||
String get taskNameRoomOwnerSendGiftUser => translate('taskNameRoomOwnerSendGiftUser');
|
String get taskNameRoomOwnerSendGiftUser =>
|
||||||
|
translate('taskNameRoomOwnerSendGiftUser');
|
||||||
|
|
||||||
String get taskNameRoomOwnerSendRedPacket => translate('taskNameRoomOwnerSendRedPacket');
|
String get taskNameRoomOwnerSendRedPacket =>
|
||||||
|
translate('taskNameRoomOwnerSendRedPacket');
|
||||||
|
|
||||||
String get taskNameRoomNewMember => translate('taskNameRoomNewMember');
|
String get taskNameRoomNewMember => translate('taskNameRoomNewMember');
|
||||||
|
|
||||||
@ -202,7 +218,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get dailyCoinBonanzaRules => translate('dailyCoinBonanzaRules');
|
String get dailyCoinBonanzaRules => translate('dailyCoinBonanzaRules');
|
||||||
|
|
||||||
String get dailyCoinBonanzaRulesDetail => translate('dailyCoinBonanzaRulesDetail');
|
String get dailyCoinBonanzaRulesDetail =>
|
||||||
|
translate('dailyCoinBonanzaRulesDetail');
|
||||||
|
|
||||||
String get personalTasks => translate('personalTasks');
|
String get personalTasks => translate('personalTasks');
|
||||||
|
|
||||||
@ -218,7 +235,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get operationFail => translate('operationFail');
|
String get operationFail => translate('operationFail');
|
||||||
|
|
||||||
String get forMoreRewardsPleaseCheckTheTaskCenter => translate('forMoreRewardsPleaseCheckTheTaskCenter');
|
String get forMoreRewardsPleaseCheckTheTaskCenter =>
|
||||||
|
translate('forMoreRewardsPleaseCheckTheTaskCenter');
|
||||||
|
|
||||||
String get likedYourComment => translate('likedYourComment');
|
String get likedYourComment => translate('likedYourComment');
|
||||||
|
|
||||||
@ -250,7 +268,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get basicFeatures => translate('basicFeatures');
|
String get basicFeatures => translate('basicFeatures');
|
||||||
|
|
||||||
String get areYouSureYouWantToDeleteYourAccount => translate('areYouSureYouWantToDeleteYourAccount');
|
String get areYouSureYouWantToDeleteYourAccount =>
|
||||||
|
translate('areYouSureYouWantToDeleteYourAccount');
|
||||||
|
|
||||||
String get game => translate('game');
|
String get game => translate('game');
|
||||||
|
|
||||||
@ -258,7 +277,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get deleteAccountTips => translate('deleteAccountTips');
|
String get deleteAccountTips => translate('deleteAccountTips');
|
||||||
|
|
||||||
String get thisUserHasBeenBlacklisted => translate('thisUserHasBeenBlacklisted');
|
String get thisUserHasBeenBlacklisted =>
|
||||||
|
translate('thisUserHasBeenBlacklisted');
|
||||||
|
|
||||||
String get comment => translate('comment');
|
String get comment => translate('comment');
|
||||||
|
|
||||||
@ -266,7 +286,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get customizedGiftRules => translate('customizedGiftRules');
|
String get customizedGiftRules => translate('customizedGiftRules');
|
||||||
|
|
||||||
String get customizedGiftRulesContent => translate('customizedGiftRulesContent');
|
String get customizedGiftRulesContent =>
|
||||||
|
translate('customizedGiftRulesContent');
|
||||||
|
|
||||||
String get clearCache => translate('clearCache');
|
String get clearCache => translate('clearCache');
|
||||||
|
|
||||||
@ -286,9 +307,11 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get enterThisVoiceChatRoom => translate('enterThisVoiceChatRoom');
|
String get enterThisVoiceChatRoom => translate('enterThisVoiceChatRoom');
|
||||||
|
|
||||||
String get swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt => translate('swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt');
|
String get swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt =>
|
||||||
|
translate('swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt');
|
||||||
|
|
||||||
String get areYouSureYouWantToClearLocalCache => translate('areYouSureYouWantToClearLocalCache');
|
String get areYouSureYouWantToClearLocalCache =>
|
||||||
|
translate('areYouSureYouWantToClearLocalCache');
|
||||||
|
|
||||||
String get multiple => translate('multiple');
|
String get multiple => translate('multiple');
|
||||||
|
|
||||||
@ -300,13 +323,17 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get clearCacheSuccessfully => translate('clearCacheSuccessfully');
|
String get clearCacheSuccessfully => translate('clearCacheSuccessfully');
|
||||||
|
|
||||||
String get successfullyAddedToTheBlacklist => translate('successfullyAddedToTheBlacklist');
|
String get successfullyAddedToTheBlacklist =>
|
||||||
|
translate('successfullyAddedToTheBlacklist');
|
||||||
|
|
||||||
String get successfullyRemovedFromTheBlacklist => translate('successfullyRemovedFromTheBlacklist');
|
String get successfullyRemovedFromTheBlacklist =>
|
||||||
|
translate('successfullyRemovedFromTheBlacklist');
|
||||||
|
|
||||||
String get successfullyRemovedFromTheDynamicBlacklist => translate('successfullyRemovedFromTheDynamicBlacklist');
|
String get successfullyRemovedFromTheDynamicBlacklist =>
|
||||||
|
translate('successfullyRemovedFromTheDynamicBlacklist');
|
||||||
|
|
||||||
String get successfullyAddedToTheDynamicBlacklist => translate('successfullyAddedToTheDynamicBlacklist');
|
String get successfullyAddedToTheDynamicBlacklist =>
|
||||||
|
translate('successfullyAddedToTheDynamicBlacklist');
|
||||||
|
|
||||||
String get areYouSureToCancelBlacklist =>
|
String get areYouSureToCancelBlacklist =>
|
||||||
translate('areYouSureToCancelBlacklist');
|
translate('areYouSureToCancelBlacklist');
|
||||||
@ -327,7 +354,8 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get userBlacklist => translate('userBlacklist');
|
String get userBlacklist => translate('userBlacklist');
|
||||||
|
|
||||||
String get areYouSureToCancelDynamicBlacklist => translate('areYouSureToCancelDynamicBlacklist');
|
String get areYouSureToCancelDynamicBlacklist =>
|
||||||
|
translate('areYouSureToCancelDynamicBlacklist');
|
||||||
|
|
||||||
String get thisFeatureIsCurrentlyUnavailable =>
|
String get thisFeatureIsCurrentlyUnavailable =>
|
||||||
translate('thisFeatureIsCurrentlyUnavailable');
|
translate('thisFeatureIsCurrentlyUnavailable');
|
||||||
@ -995,7 +1023,6 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get charmGameRulesTips => translate('charmGameRulesTips');
|
String get charmGameRulesTips => translate('charmGameRulesTips');
|
||||||
|
|
||||||
|
|
||||||
String get enterYourNewPassword => translate('enterYourNewPassword');
|
String get enterYourNewPassword => translate('enterYourNewPassword');
|
||||||
|
|
||||||
String get confirmSwitchMicThemeTips =>
|
String get confirmSwitchMicThemeTips =>
|
||||||
@ -1063,13 +1090,11 @@ class SCAppLocalizations {
|
|||||||
String vipEmoticon(String name) =>
|
String vipEmoticon(String name) =>
|
||||||
translate('vipEmoticon').replaceAll('{1}', name);
|
translate('vipEmoticon').replaceAll('{1}', name);
|
||||||
|
|
||||||
String family3(String name) =>
|
String family3(String name) => translate('family3').replaceAll('{1}', name);
|
||||||
translate('family3').replaceAll('{1}', name);
|
|
||||||
|
|
||||||
String onlineUsers(String name1, String name2) =>
|
String onlineUsers(String name1, String name2) => translate(
|
||||||
translate(
|
'onlineUsers',
|
||||||
'onlineUsers',
|
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
||||||
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
|
||||||
|
|
||||||
String timeSpentTogether(String name) =>
|
String timeSpentTogether(String name) =>
|
||||||
translate('timeSpentTogether').replaceAll('{1}', name);
|
translate('timeSpentTogether').replaceAll('{1}', name);
|
||||||
@ -1086,10 +1111,9 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String win2(String name) => translate('win2').replaceAll('{1}', name);
|
String win2(String name) => translate('win2').replaceAll('{1}', name);
|
||||||
|
|
||||||
String numberOfMyCPs(String name1, String name2) =>
|
String numberOfMyCPs(String name1, String name2) => translate(
|
||||||
translate(
|
'numberOfMyCPs',
|
||||||
'numberOfMyCPs',
|
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
||||||
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
|
||||||
|
|
||||||
String sendUserId(String name) =>
|
String sendUserId(String name) =>
|
||||||
translate('sendUserId').replaceAll('{1}', name);
|
translate('sendUserId').replaceAll('{1}', name);
|
||||||
@ -1466,6 +1490,9 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get skip2 => translate('skip2');
|
String get skip2 => translate('skip2');
|
||||||
|
|
||||||
|
String skipCountdown(String seconds) =>
|
||||||
|
translate('skipCountdown').replaceAll('{1}', seconds);
|
||||||
|
|
||||||
String get updateNow => translate('updateNow');
|
String get updateNow => translate('updateNow');
|
||||||
|
|
||||||
String get clearMessage => translate('clearMessage');
|
String get clearMessage => translate('clearMessage');
|
||||||
@ -1505,13 +1532,14 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String xxfamily(String name) => translate('xxfamily').replaceAll('{1}', name);
|
String xxfamily(String name) => translate('xxfamily').replaceAll('{1}', name);
|
||||||
|
|
||||||
String collectionTimeTips(String time,
|
String collectionTimeTips(
|
||||||
String remainCount,
|
String time,
|
||||||
String totalCount,) =>
|
String remainCount,
|
||||||
translate('collectionTimeTips')
|
String totalCount,
|
||||||
.replaceAll('{1}', time)
|
) => translate('collectionTimeTips')
|
||||||
.replaceAll("{2}", remainCount)
|
.replaceAll('{1}', time)
|
||||||
.replaceAll("{3}", totalCount);
|
.replaceAll("{2}", remainCount)
|
||||||
|
.replaceAll("{3}", totalCount);
|
||||||
|
|
||||||
String yourVipWillExpire(String name) =>
|
String yourVipWillExpire(String name) =>
|
||||||
translate('yourVipWillExpire').replaceAll('{1}', name);
|
translate('yourVipWillExpire').replaceAll('{1}', name);
|
||||||
@ -1522,10 +1550,9 @@ class SCAppLocalizations {
|
|||||||
String privileges(String name) =>
|
String privileges(String name) =>
|
||||||
translate('privileges').replaceAll('{1}', name);
|
translate('privileges').replaceAll('{1}', name);
|
||||||
|
|
||||||
String remainingNumberTips(String name1, String name2) =>
|
String remainingNumberTips(String name1, String name2) => translate(
|
||||||
translate(
|
'remainingNumberTips',
|
||||||
'remainingNumberTips',
|
).replaceAll('{1}', name1).replaceAll("{2}", name2);
|
||||||
).replaceAll('{1}', name1).replaceAll("{2}", name2);
|
|
||||||
|
|
||||||
String coins2(String name) => translate('coins2').replaceAll('{1}', name);
|
String coins2(String name) => translate('coins2').replaceAll('{1}', name);
|
||||||
|
|
||||||
@ -1548,18 +1575,16 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String skip(String name) => translate('skip').replaceAll('{1}', name);
|
String skip(String name) => translate('skip').replaceAll('{1}', name);
|
||||||
|
|
||||||
String familyMember2(String name1, String name2) =>
|
String familyMember2(String name1, String name2) => translate(
|
||||||
translate(
|
'familyMember2',
|
||||||
'familyMember2',
|
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
||||||
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
|
||||||
|
|
||||||
String familyMember3(String name1) =>
|
String familyMember3(String name1) =>
|
||||||
translate('familyMember3').replaceAll('{1}', name1);
|
translate('familyMember3').replaceAll('{1}', name1);
|
||||||
|
|
||||||
String familyAdmin2(String name1, String name2) =>
|
String familyAdmin2(String name1, String name2) => translate(
|
||||||
translate(
|
'familyAdmin2',
|
||||||
'familyAdmin2',
|
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
||||||
).replaceAll('{1}', name1).replaceAll('{2}', name2);
|
|
||||||
|
|
||||||
String follow2(String name) => translate('follow2').replaceAll('{1}', name);
|
String follow2(String name) => translate('follow2').replaceAll('{1}', name);
|
||||||
|
|
||||||
@ -1586,7 +1611,7 @@ class _SCAppLocalizationsDelegate
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool isSupported(Locale locale) =>
|
bool isSupported(Locale locale) =>
|
||||||
['en', 'zh', 'ar','bn','tr'].contains(locale.languageCode);
|
['en', 'zh', 'ar', 'bn', 'tr'].contains(locale.languageCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SCAppLocalizations> load(Locale locale) async {
|
Future<SCAppLocalizations> load(Locale locale) async {
|
||||||
|
|||||||
105
lib/modules/splash/last_weekly_cp_splash_cache.dart
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
|
|
||||||
|
class LastWeeklyCPSplashEntry {
|
||||||
|
const LastWeeklyCPSplashEntry({
|
||||||
|
required this.rank,
|
||||||
|
required this.leftAvatarUrl,
|
||||||
|
required this.rightAvatarUrl,
|
||||||
|
required this.leftNickname,
|
||||||
|
required this.rightNickname,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int rank;
|
||||||
|
final String leftAvatarUrl;
|
||||||
|
final String rightAvatarUrl;
|
||||||
|
final String leftNickname;
|
||||||
|
final String rightNickname;
|
||||||
|
|
||||||
|
factory LastWeeklyCPSplashEntry.fromJson(Map<String, dynamic> json) {
|
||||||
|
return LastWeeklyCPSplashEntry(
|
||||||
|
rank: (json['rank'] as num?)?.toInt() ?? 0,
|
||||||
|
leftAvatarUrl: (json['leftAvatarUrl'] as String?) ?? '',
|
||||||
|
rightAvatarUrl: (json['rightAvatarUrl'] as String?) ?? '',
|
||||||
|
leftNickname: (json['leftNickname'] as String?) ?? '',
|
||||||
|
rightNickname: (json['rightNickname'] as String?) ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'rank': rank,
|
||||||
|
'leftAvatarUrl': leftAvatarUrl,
|
||||||
|
'rightAvatarUrl': rightAvatarUrl,
|
||||||
|
'leftNickname': leftNickname,
|
||||||
|
'rightNickname': rightNickname,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LastWeeklyCPSplashCache {
|
||||||
|
static const String _cacheKey = 'last_weekly_cp_splash_cache_v1';
|
||||||
|
|
||||||
|
/// 搜索 `api-ready-launch-splash` 可以快速找到后续要恢复接入的位置。
|
||||||
|
///
|
||||||
|
/// 当前 CP 榜接口还没 ready,因此先彻底关闭这套自定义启动页。
|
||||||
|
/// 正式恢复时保持“本地有缓存才展示、无缓存不展示”的逻辑,不要再写占位缓存。
|
||||||
|
static const bool _allowCacheDrivenSplash = false;
|
||||||
|
|
||||||
|
static bool get shouldShow {
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return loadCachedEntries().isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<LastWeeklyCPSplashEntry> loadDisplayEntries() {
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
return loadCachedEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<LastWeeklyCPSplashEntry> loadCachedEntries() {
|
||||||
|
final raw = DataPersistence.getString(_cacheKey);
|
||||||
|
if (raw.isEmpty) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(raw);
|
||||||
|
if (decoded is! List) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
return decoded
|
||||||
|
.whereType<Map>()
|
||||||
|
.map(
|
||||||
|
(item) => LastWeeklyCPSplashEntry.fromJson(
|
||||||
|
Map<String, dynamic>.from(item),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList()
|
||||||
|
..sort((a, b) => a.rank.compareTo(b.rank));
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('LastWeeklyCPSplashCache parse failed: $error\n$stackTrace');
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> refreshCacheInBackground() async {
|
||||||
|
// TODO(api-ready-launch-splash): CP 榜接口 ready 后,把 `_allowCacheDrivenSplash`
|
||||||
|
// 改为 true,并在这里补上“接口取数 -> 映射 LastWeeklyCPSplashEntry ->
|
||||||
|
// 写入 `_cacheKey`”的流程。保留“有缓存才展示、无缓存不展示”的正式逻辑。
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 当前占位逻辑已下线,等待正式 CP 榜接口接入。
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('LastWeeklyCPSplashCache refresh failed: $error\n$stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,67 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||||
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/shared/tools/sc_version_utils.dart';
|
import 'package:yumi/shared/tools/sc_version_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/app/routes/sc_routes.dart';
|
import 'package:yumi/app/routes/sc_routes.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
||||||
import 'package:yumi/modules/auth/login_route.dart';
|
import 'package:yumi/modules/auth/login_route.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
|
|
||||||
|
import 'last_weekly_cp_splash_cache.dart';
|
||||||
|
import 'weekly_star_splash_cache.dart';
|
||||||
|
|
||||||
|
enum _SplashPresentationVariant { weeklyStar, lastWeeklyCp, defaultSplash }
|
||||||
|
|
||||||
|
/// CP 启动页手动微调区。
|
||||||
|
/// 如果后续还要继续对齐设计稿,优先只改这一组 Rect / Size,
|
||||||
|
/// 不要去动 _buildCpRankCard 里的层级和结构。
|
||||||
|
class _LastWeeklyCpLayout {
|
||||||
|
static const Rect rank1FrameRect = Rect.fromLTWH(40, 253, 297, 148);
|
||||||
|
static const Rect rank2FrameRect = Rect.fromLTWH(0, 427, 196, 111);
|
||||||
|
static const Rect rank3FrameRect = Rect.fromLTWH(179, 427, 196, 111);
|
||||||
|
|
||||||
|
static final Rect rank1LeftAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(151, 353),
|
||||||
|
width: 58,
|
||||||
|
height: 58,
|
||||||
|
);
|
||||||
|
static final Rect rank1RightAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(227, 352),
|
||||||
|
width: 58,
|
||||||
|
height: 58,
|
||||||
|
);
|
||||||
|
static const Rect rank1LabelRect = Rect.fromLTWH(118, 383, 140, 22);
|
||||||
|
|
||||||
|
static final Rect rank2LeftAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(46, 508),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
);
|
||||||
|
static final Rect rank2RightAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(123, 509),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
);
|
||||||
|
static const Rect rank2LabelRect = Rect.fromLTWH(10, 533, 145, 26);
|
||||||
|
|
||||||
|
static final Rect rank3LeftAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(252, 508),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
);
|
||||||
|
static final Rect rank3RightAvatarRect = Rect.fromCenter(
|
||||||
|
center: const Offset(329, 509),
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
);
|
||||||
|
static const Rect rank3LabelRect = Rect.fromLTWH(218, 533, 145, 26);
|
||||||
|
}
|
||||||
|
|
||||||
class SplashPage extends StatefulWidget {
|
class SplashPage extends StatefulWidget {
|
||||||
const SplashPage({super.key});
|
const SplashPage({super.key});
|
||||||
@ -17,15 +71,28 @@ class SplashPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SplashPageState extends State<SplashPage> {
|
class _SplashPageState extends State<SplashPage> {
|
||||||
static const Duration _minimumSplashDuration = Duration(milliseconds: 900);
|
static const Duration _minimumSplashDuration = Duration(seconds: 3);
|
||||||
|
static const Size _weeklyStarCanvasSize = Size(375, 812);
|
||||||
|
static const Size _lastWeeklyCpCanvasSize = Size(375, 812);
|
||||||
|
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
Timer? _countdownTimer;
|
||||||
|
late final List<WeeklyStarSplashEntry> _weeklyStarEntries;
|
||||||
|
late final List<LastWeeklyCPSplashEntry> _lastWeeklyCpEntries;
|
||||||
|
late final _SplashPresentationVariant _selectedVariant;
|
||||||
|
int _remainingSeconds = _minimumSplashDuration.inSeconds;
|
||||||
|
bool _isNavigating = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_weeklyStarEntries = WeeklyStarSplashCache.loadDisplayEntries();
|
||||||
|
_lastWeeklyCpEntries = LastWeeklyCPSplashCache.loadDisplayEntries();
|
||||||
|
_selectedVariant = _pickSplashVariant();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
unawaited(FileCacheManager.getInstance().getFilePath());
|
unawaited(FileCacheManager.getInstance().getFilePath());
|
||||||
|
unawaited(WeeklyStarSplashCache.refreshCacheInBackground());
|
||||||
|
unawaited(LastWeeklyCPSplashCache.refreshCacheInBackground());
|
||||||
});
|
});
|
||||||
_startNavigationTimer();
|
_startNavigationTimer();
|
||||||
}
|
}
|
||||||
@ -38,36 +105,341 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
|
|
||||||
/// 启动页最短展示时间
|
/// 启动页最短展示时间
|
||||||
void _startNavigationTimer() {
|
void _startNavigationTimer() {
|
||||||
_timer = Timer(_minimumSplashDuration, () {
|
_remainingSeconds = _minimumSplashDuration.inSeconds;
|
||||||
if (mounted) {
|
_timer = Timer(_minimumSplashDuration, _dismissSplash);
|
||||||
_goMainPage();
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
final nextSeconds = _minimumSplashDuration.inSeconds - timer.tick;
|
||||||
|
if (nextSeconds <= 0) {
|
||||||
|
timer.cancel();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_remainingSeconds = nextSeconds;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final splashContent = switch (_selectedVariant) {
|
||||||
|
_SplashPresentationVariant.weeklyStar => _buildWeeklyStarSplash(),
|
||||||
|
_SplashPresentationVariant.lastWeeklyCp => _buildLastWeeklyCpSplash(),
|
||||||
|
_SplashPresentationVariant.defaultSplash => _buildDefaultSplash(),
|
||||||
|
};
|
||||||
|
final showLaunchOverlay =
|
||||||
|
_selectedVariant != _SplashPresentationVariant.defaultSplash;
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
|
color: Colors.black,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
splashContent,
|
||||||
SCGlobalConfig.businessLogicStrategy.getSplashPageBackgroundImage(),
|
if (showLaunchOverlay) _buildSkipCountdownButton(context),
|
||||||
width: ScreenUtil().screenWidth,
|
|
||||||
height: ScreenUtil().screenHeight,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
Image.asset(
|
|
||||||
SCGlobalConfig.businessLogicStrategy.getSplashPageIcon(),
|
|
||||||
width: 107.w,
|
|
||||||
height: 159.w,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _goMainPage() async {
|
_SplashPresentationVariant _pickSplashVariant() {
|
||||||
|
final variants = <_SplashPresentationVariant>[];
|
||||||
|
if (WeeklyStarSplashCache.shouldShow) {
|
||||||
|
variants.add(_SplashPresentationVariant.weeklyStar);
|
||||||
|
}
|
||||||
|
if (LastWeeklyCPSplashCache.shouldShow) {
|
||||||
|
variants.add(_SplashPresentationVariant.lastWeeklyCp);
|
||||||
|
}
|
||||||
|
if (variants.isEmpty) {
|
||||||
|
return _SplashPresentationVariant.defaultSplash;
|
||||||
|
}
|
||||||
|
return variants[Random().nextInt(variants.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDefaultSplash() {
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
SCGlobalConfig.businessLogicStrategy.getSplashPageBackgroundImage(),
|
||||||
|
width: ScreenUtil().screenWidth,
|
||||||
|
height: ScreenUtil().screenHeight,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
Image.asset(
|
||||||
|
SCGlobalConfig.businessLogicStrategy.getSplashPageIcon(),
|
||||||
|
width: 107.w,
|
||||||
|
height: 159.w,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSkipCountdownButton(BuildContext context) {
|
||||||
|
final localizations = SCAppLocalizations.of(context);
|
||||||
|
final label =
|
||||||
|
localizations?.skipCountdown(_remainingSeconds.toString()) ??
|
||||||
|
'Skip ${_remainingSeconds}s';
|
||||||
|
final borderRadius = BorderRadius.circular(999);
|
||||||
|
|
||||||
|
return SafeArea(
|
||||||
|
child: Align(
|
||||||
|
alignment: AlignmentDirectional.topEnd,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsetsDirectional.only(top: 12.h, end: 16.w),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Ink(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
colors: [Color(0x33FFFFFF), Color(0x18FFFFFF)],
|
||||||
|
begin: AlignmentDirectional.centerStart,
|
||||||
|
end: AlignmentDirectional.centerEnd,
|
||||||
|
),
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
border: Border.all(color: Colors.white.withValues(alpha: 0.18)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.1),
|
||||||
|
blurRadius: 10.w,
|
||||||
|
offset: Offset(0, 3.w),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
onTap: _dismissSplash,
|
||||||
|
splashFactory: NoSplash.splashFactory,
|
||||||
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
|
splashColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 12.w,
|
||||||
|
vertical: 8.h,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
maxLines: 1,
|
||||||
|
softWrap: false,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withValues(alpha: 0.96),
|
||||||
|
fontSize: 12.sp,
|
||||||
|
height: 1,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWeeklyStarSplash() {
|
||||||
|
return SizedBox.expand(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
child: SizedBox(
|
||||||
|
width: _weeklyStarCanvasSize.width,
|
||||||
|
height: _weeklyStarCanvasSize.height,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.asset(
|
||||||
|
'sc_images/splash/sc_weekly_star_bg.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildRankCard(
|
||||||
|
entry: _entryForRank(1),
|
||||||
|
frameAsset: 'sc_images/splash/sc_icon_weekly_star_rank_1.png',
|
||||||
|
frameRect: const Rect.fromLTWH(24, 110, 327, 248),
|
||||||
|
avatarRect: Rect.fromCenter(
|
||||||
|
center: Offset(188, 261),
|
||||||
|
width: 74,
|
||||||
|
height: 74,
|
||||||
|
),
|
||||||
|
labelRect: const Rect.fromLTWH(154, 328, 67, 12),
|
||||||
|
),
|
||||||
|
_buildRankCard(
|
||||||
|
entry: _entryForRank(2),
|
||||||
|
frameAsset: 'sc_images/splash/sc_icon_weekly_star_rank_2.png',
|
||||||
|
frameRect: const Rect.fromLTWH(0, 376, 182, 115),
|
||||||
|
avatarRect: Rect.fromCenter(
|
||||||
|
center: Offset(77, 437),
|
||||||
|
width: 62,
|
||||||
|
height: 62,
|
||||||
|
),
|
||||||
|
labelRect: const Rect.fromLTWH(40, 501, 74, 12),
|
||||||
|
),
|
||||||
|
_buildRankCard(
|
||||||
|
entry: _entryForRank(3),
|
||||||
|
frameAsset: 'sc_images/splash/sc_icon_weekly_star_rank_3.png',
|
||||||
|
frameRect: const Rect.fromLTWH(180, 376, 195, 117),
|
||||||
|
avatarRect: Rect.fromCenter(
|
||||||
|
center: Offset(299, 439),
|
||||||
|
width: 66,
|
||||||
|
height: 66,
|
||||||
|
),
|
||||||
|
labelRect: const Rect.fromLTWH(260, 501, 78, 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLastWeeklyCpSplash() {
|
||||||
|
return SizedBox.expand(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
child: SizedBox(
|
||||||
|
width: _lastWeeklyCpCanvasSize.width,
|
||||||
|
height: _lastWeeklyCpCanvasSize.height,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.asset(
|
||||||
|
'sc_images/splash/sc_last_weekly_cp_bg.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildCpRankCard(
|
||||||
|
entry: _cpEntryForRank(1),
|
||||||
|
frameAsset:
|
||||||
|
'sc_images/splash/sc_icon_last_weekly_cp_rank_1.png',
|
||||||
|
frameRect: _LastWeeklyCpLayout.rank1FrameRect,
|
||||||
|
leftAvatarRect: _LastWeeklyCpLayout.rank1LeftAvatarRect,
|
||||||
|
rightAvatarRect: _LastWeeklyCpLayout.rank1RightAvatarRect,
|
||||||
|
labelRect: _LastWeeklyCpLayout.rank1LabelRect,
|
||||||
|
),
|
||||||
|
_buildCpRankCard(
|
||||||
|
entry: _cpEntryForRank(2),
|
||||||
|
frameAsset:
|
||||||
|
'sc_images/splash/sc_icon_last_weekly_cp_rank_2.png',
|
||||||
|
frameRect: _LastWeeklyCpLayout.rank2FrameRect,
|
||||||
|
leftAvatarRect: _LastWeeklyCpLayout.rank2LeftAvatarRect,
|
||||||
|
rightAvatarRect: _LastWeeklyCpLayout.rank2RightAvatarRect,
|
||||||
|
labelRect: _LastWeeklyCpLayout.rank2LabelRect,
|
||||||
|
),
|
||||||
|
_buildCpRankCard(
|
||||||
|
entry: _cpEntryForRank(3),
|
||||||
|
frameAsset:
|
||||||
|
'sc_images/splash/sc_icon_last_weekly_cp_rank_3.png',
|
||||||
|
frameRect: _LastWeeklyCpLayout.rank3FrameRect,
|
||||||
|
leftAvatarRect: _LastWeeklyCpLayout.rank3LeftAvatarRect,
|
||||||
|
rightAvatarRect: _LastWeeklyCpLayout.rank3RightAvatarRect,
|
||||||
|
labelRect: _LastWeeklyCpLayout.rank3LabelRect,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WeeklyStarSplashEntry _entryForRank(int rank) {
|
||||||
|
return _weeklyStarEntries.firstWhere(
|
||||||
|
(entry) => entry.rank == rank,
|
||||||
|
orElse:
|
||||||
|
() => WeeklyStarSplashEntry(
|
||||||
|
rank: rank,
|
||||||
|
avatarUrl: '',
|
||||||
|
nickname: 'Namename',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LastWeeklyCPSplashEntry _cpEntryForRank(int rank) {
|
||||||
|
return _lastWeeklyCpEntries.firstWhere(
|
||||||
|
(entry) => entry.rank == rank,
|
||||||
|
orElse:
|
||||||
|
() => LastWeeklyCPSplashEntry(
|
||||||
|
rank: rank,
|
||||||
|
leftAvatarUrl: '',
|
||||||
|
rightAvatarUrl: '',
|
||||||
|
leftNickname: 'Namename',
|
||||||
|
rightNickname: 'Namename',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRankCard({
|
||||||
|
required WeeklyStarSplashEntry entry,
|
||||||
|
required String frameAsset,
|
||||||
|
required Rect frameRect,
|
||||||
|
required Rect avatarRect,
|
||||||
|
required Rect labelRect,
|
||||||
|
}) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: avatarRect,
|
||||||
|
child: head(url: entry.avatarUrl, width: avatarRect.width),
|
||||||
|
),
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: frameRect,
|
||||||
|
child: Image.asset(frameAsset, fit: BoxFit.fill),
|
||||||
|
),
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: labelRect,
|
||||||
|
child: _SplashOutlinedText(text: entry.nickname),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCpRankCard({
|
||||||
|
required LastWeeklyCPSplashEntry entry,
|
||||||
|
required String frameAsset,
|
||||||
|
required Rect frameRect,
|
||||||
|
required Rect leftAvatarRect,
|
||||||
|
required Rect rightAvatarRect,
|
||||||
|
required Rect labelRect,
|
||||||
|
}) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: frameRect,
|
||||||
|
child: Image.asset(frameAsset, fit: BoxFit.fill),
|
||||||
|
),
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: leftAvatarRect,
|
||||||
|
child: head(url: entry.leftAvatarUrl, width: leftAvatarRect.width),
|
||||||
|
),
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: rightAvatarRect,
|
||||||
|
child: head(url: entry.rightAvatarUrl, width: rightAvatarRect.width),
|
||||||
|
),
|
||||||
|
Positioned.fromRect(
|
||||||
|
rect: labelRect,
|
||||||
|
child: _LastWeeklyCpPairLabel(
|
||||||
|
leftText: entry.leftNickname,
|
||||||
|
rightText: entry.rightNickname,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _dismissSplash() {
|
||||||
|
if (_isNavigating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isNavigating = true;
|
||||||
|
_cancelTimer();
|
||||||
|
unawaited(_goMainPage());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _goMainPage() async {
|
||||||
try {
|
try {
|
||||||
await SCVersionUtils.checkReview();
|
await SCVersionUtils.checkReview();
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
@ -92,5 +464,83 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
void _cancelTimer() {
|
void _cancelTimer() {
|
||||||
// 计时器(`Timer`)组件的取消(`cancel`)方法,取消计时器。
|
// 计时器(`Timer`)组件的取消(`cancel`)方法,取消计时器。
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
|
_countdownTimer?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashOutlinedText extends StatelessWidget {
|
||||||
|
const _SplashOutlinedText({required this.text});
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
static const Color _fillColor = Color(0xFFA11726);
|
||||||
|
static const Color _strokeColor = Color(0xFFFFEBBA);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final displayText = text.trim().isEmpty ? 'Namename' : text.trim();
|
||||||
|
return FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
displayText,
|
||||||
|
maxLines: 1,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
height: 1,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
foreground:
|
||||||
|
Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 1
|
||||||
|
..color = _strokeColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
displayText,
|
||||||
|
maxLines: 1,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
height: 1,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: _fillColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LastWeeklyCpPairLabel extends StatelessWidget {
|
||||||
|
const _LastWeeklyCpPairLabel({
|
||||||
|
required this.leftText,
|
||||||
|
required this.rightText,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String leftText;
|
||||||
|
final String rightText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: _SplashOutlinedText(text: leftText),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: _SplashOutlinedText(text: rightText),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
148
lib/modules/splash/weekly_star_splash_cache.dart
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/sc_top_four_with_reward_res.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_config_repository_imp.dart';
|
||||||
|
|
||||||
|
class WeeklyStarSplashEntry {
|
||||||
|
const WeeklyStarSplashEntry({
|
||||||
|
required this.rank,
|
||||||
|
required this.avatarUrl,
|
||||||
|
required this.nickname,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int rank;
|
||||||
|
final String avatarUrl;
|
||||||
|
final String nickname;
|
||||||
|
|
||||||
|
factory WeeklyStarSplashEntry.fromJson(Map<String, dynamic> json) {
|
||||||
|
return WeeklyStarSplashEntry(
|
||||||
|
rank: (json['rank'] as num?)?.toInt() ?? 0,
|
||||||
|
avatarUrl: (json['avatarUrl'] as String?) ?? '',
|
||||||
|
nickname: (json['nickname'] as String?) ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {'rank': rank, 'avatarUrl': avatarUrl, 'nickname': nickname};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WeeklyStarSplashCache {
|
||||||
|
static const String _cacheKey = 'weekly_star_splash_cache_v1';
|
||||||
|
|
||||||
|
/// 搜索 `api-ready-launch-splash` 可以快速找到后续要恢复接入的位置。
|
||||||
|
///
|
||||||
|
/// 当前周榜接口链路还未 ready,因此先关闭这套自定义启动页。
|
||||||
|
/// 正式恢复时保持“本地有缓存才展示、无缓存不展示”的逻辑,不要再回退到预览数据。
|
||||||
|
static const bool _allowCacheDrivenSplash = false;
|
||||||
|
|
||||||
|
static bool get shouldShow {
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return loadCachedEntries().isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WeeklyStarSplashEntry> loadDisplayEntries() {
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
return loadCachedEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WeeklyStarSplashEntry> loadCachedEntries() {
|
||||||
|
final raw = DataPersistence.getString(_cacheKey);
|
||||||
|
if (raw.isEmpty) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(raw);
|
||||||
|
if (decoded is! List) {
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
return decoded
|
||||||
|
.whereType<Map>()
|
||||||
|
.map(
|
||||||
|
(item) =>
|
||||||
|
WeeklyStarSplashEntry.fromJson(Map<String, dynamic>.from(item)),
|
||||||
|
)
|
||||||
|
.toList()
|
||||||
|
..sort((a, b) => a.rank.compareTo(b.rank));
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('WeeklyStarSplashCache parse failed: $error\n$stackTrace');
|
||||||
|
return const [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> refreshCacheInBackground() async {
|
||||||
|
// TODO(api-ready-launch-splash): 周榜接口 ready 后,把 `_allowCacheDrivenSplash`
|
||||||
|
// 改为 true,并继续只在这里写入真实接口结果到 `_cacheKey`。
|
||||||
|
// 这样就能保持“有缓存才展示、无缓存不展示”的正式逻辑。
|
||||||
|
if (!_allowCacheDrivenSplash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final result = await SCConfigRepositoryImp().topFourWithReward();
|
||||||
|
final entries = _mapTopThree(result);
|
||||||
|
if (entries.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await DataPersistence.setString(
|
||||||
|
_cacheKey,
|
||||||
|
jsonEncode(entries.map((entry) => entry.toJson()).toList()),
|
||||||
|
);
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
debugPrint('WeeklyStarSplashCache refresh failed: $error\n$stackTrace');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WeeklyStarSplashEntry> _mapTopThree(
|
||||||
|
List<SCTopFourWithRewardRes> result,
|
||||||
|
) {
|
||||||
|
final topThree =
|
||||||
|
result
|
||||||
|
.where(
|
||||||
|
(item) =>
|
||||||
|
(item.avatar ?? '').trim().isNotEmpty ||
|
||||||
|
(item.nickname ?? '').trim().isNotEmpty,
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(item) => WeeklyStarSplashEntry(
|
||||||
|
rank: (item.rank ?? 0).toInt(),
|
||||||
|
avatarUrl: (item.avatar ?? '').trim(),
|
||||||
|
nickname: (item.nickname ?? '').trim(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList()
|
||||||
|
..sort((a, b) => a.rank.compareTo(b.rank));
|
||||||
|
|
||||||
|
return _ensureThreeEntries(topThree.take(3).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WeeklyStarSplashEntry> _ensureThreeEntries(
|
||||||
|
List<WeeklyStarSplashEntry> entries,
|
||||||
|
) {
|
||||||
|
final normalized = List<WeeklyStarSplashEntry>.from(entries)
|
||||||
|
..sort((a, b) => a.rank.compareTo(b.rank));
|
||||||
|
|
||||||
|
for (var rank = 1; rank <= 3; rank++) {
|
||||||
|
final hasRank = normalized.any((entry) => entry.rank == rank);
|
||||||
|
if (!hasRank) {
|
||||||
|
normalized.add(
|
||||||
|
WeeklyStarSplashEntry(
|
||||||
|
rank: rank,
|
||||||
|
avatarUrl: '',
|
||||||
|
nickname: 'Namename',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized.sort((a, b) => a.rank.compareTo(b.rank));
|
||||||
|
return normalized.take(3).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
sc_images/splash/sc_icon_last_weekly_cp_rank_1.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
sc_images/splash/sc_icon_last_weekly_cp_rank_2.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
sc_images/splash/sc_icon_last_weekly_cp_rank_3.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
sc_images/splash/sc_icon_weekly_star_rank_1.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
sc_images/splash/sc_icon_weekly_star_rank_2.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
sc_images/splash/sc_icon_weekly_star_rank_3.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
sc_images/splash/sc_last_weekly_cp_bg.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
sc_images/splash/sc_weekly_star_bg.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
20
需求进度.md
@ -4,7 +4,14 @@
|
|||||||
- 控制当前 Flutter Android 发包体积,持续定位冗余组件、超大资源和不合理构建配置,并把每一步处理结果落盘记录。
|
- 控制当前 Flutter Android 发包体积,持续定位冗余组件、超大资源和不合理构建配置,并把每一步处理结果落盘记录。
|
||||||
|
|
||||||
## 本轮启动优化(非网络)
|
## 本轮启动优化(非网络)
|
||||||
- 已移除启动页固定 `6` 秒倒计时,改为最短约 `900ms` 展示后继续路由判断,避免人为等待直接拉长入 app 时间。
|
- 已补回启动页正式展示逻辑:当前 `Weekly Star / Last Weekly CP` 两套自定义启动页都改为“本地有缓存才展示、无缓存不展示”;由于现阶段周榜与 CP 榜接口链路都还未 ready,缓存刷新逻辑已先关闭,所以当前启动阶段会直接回退到默认 splash,不再展示这两套定制视觉稿。相关恢复入口已在缓存类里用 `api-ready-launch-splash` 注释标记,后续接口 ready 后可直接搜索接回。
|
||||||
|
- 已将启动页展示时间收敛为 `3` 秒,并在右上角新增通用 `skip 倒计时` 按钮:当前按钮会按秒级动态展示剩余时间,点击可立即跳过;文案已补齐 `en/ar/tr/bn` 多语言翻译,并按 locale 输出倒计时文本,便于后续继续做 RTL 语言验收。
|
||||||
|
- 已按 2026-04-20 最新截图反馈重新校准 `Last Weekly CP` 启动页的头像与昵称区域,并在 `SplashPage` 中单独抽出 `_LastWeeklyCpLayout` 手动微调区;后续如果还要继续挪位置,直接改榜一/榜二/榜三的 `frame/avatar/label Rect` 即可,不用再翻布局层级。
|
||||||
|
- 已按 2026-04-20 最新确认把 `Last Weekly CP` 启动页临时切到“通用头像占位”模式:由于当前 CP 榜正式接口还未就绪,启动页不再读取本地 `cpList` 或当前用户头像,现统一使用默认头像占位与通用昵称,专门用于先校对双人头像位和昵称区域的 UI 位置;后续接口 ready 后再切回真实数据源。
|
||||||
|
- 已继续扩展启动页方案:桌面 `启动页_cp榜` 的背景图与 CP 榜一/榜二/榜三素材已导入工程,并新增 `Last Weekly CP` 启动页;当前 `SplashPage` 会在 `Weekly Star` 与 `Last Weekly CP` 两套视觉稿之间随机选一张展示,便于一起校对两套 UI 样式。
|
||||||
|
- 已为 `Last Weekly CP` 启动页补齐本地缓存骨架:新增独立的 CP 榜缓存结构,当前会先把默认占位头像与通用昵称写入本地缓存,后续若改成正式的“有相关缓存才启用”或切到独立榜单来源,只需要替换数据入口,不用重做随机展示和布局层。
|
||||||
|
- 已按 2026-04-20 新需求在 Flutter 启动页接入 `Weekly Star` 周榜视觉稿:桌面 `启动页_周榜` 的背景图与榜一/榜二/榜三翅膀框素材已导入工程,并替换了原先仅展示静态 logo 的 `SplashPage`,当前会按参考图在启动阶段叠加 3 个头像位和用户名描边文案,便于先校对 UI 样式。
|
||||||
|
- 已为 `Weekly Star` 启动页补齐本地缓存读写骨架:新增独立缓存辅助类,支持把 `/ranking/top-four-with-reward` 返回的榜单前三头像/昵称写入本地,并在启动时优先读取缓存数据渲染;后续只需要把“当前固定启用”切回“缓存存在时才启用”即可,不用重做布局层。
|
||||||
- 已将 Firebase / Crashlytics 从 `runApp` 前阻塞初始化改为首帧后后台预热,并补充“未就绪时仅本地打印”的异常兜底,减少首屏前平台初始化阻塞。
|
- 已将 Firebase / Crashlytics 从 `runApp` 前阻塞初始化改为首帧后后台预热,并补充“未就绪时仅本地打印”的异常兜底,减少首屏前平台初始化阻塞。
|
||||||
- 已将 `SocialChatAuthenticationManager`、`SocialChatUserProfileManager` 改为按需创建,避免 splash 阶段就提前触发 Google Sign-In / Firebase 会话检查。
|
- 已将 `SocialChatAuthenticationManager`、`SocialChatUserProfileManager` 改为按需创建,避免 splash 阶段就提前触发 Google Sign-In / Firebase 会话检查。
|
||||||
- 已将 deep link、设备信息、缓存目录创建等非首帧必需动作延后到首帧后执行,降低首屏竞争。
|
- 已将 deep link、设备信息、缓存目录创建等非首帧必需动作延后到首帧后执行,降低首屏竞争。
|
||||||
@ -162,6 +169,9 @@
|
|||||||
- `lib/modules/user/edit/edit_user_info_page2.dart`
|
- `lib/modules/user/edit/edit_user_info_page2.dart`
|
||||||
- `lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart`
|
- `lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart`
|
||||||
- `lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart`
|
- `lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart`
|
||||||
|
- `lib/modules/splash/splash_page.dart`
|
||||||
|
- `lib/modules/splash/last_weekly_cp_splash_cache.dart`
|
||||||
|
- `lib/modules/splash/weekly_star_splash_cache.dart`
|
||||||
- `lib/shared/tools/sc_room_profile_cache.dart`
|
- `lib/shared/tools/sc_room_profile_cache.dart`
|
||||||
- `lib/shared/business_logic/models/res/room_res.dart`
|
- `lib/shared/business_logic/models/res/room_res.dart`
|
||||||
- `lib/shared/business_logic/models/res/follow_room_res.dart`
|
- `lib/shared/business_logic/models/res/follow_room_res.dart`
|
||||||
@ -193,6 +203,14 @@
|
|||||||
- `lib/modules/user/my_items/theme/bags_theme_page.dart`
|
- `lib/modules/user/my_items/theme/bags_theme_page.dart`
|
||||||
- `lib/ui_kit/widgets/store/store_bag_page_helpers.dart`
|
- `lib/ui_kit/widgets/store/store_bag_page_helpers.dart`
|
||||||
- `sc_images/general/sc_no_data.png`
|
- `sc_images/general/sc_no_data.png`
|
||||||
|
- `sc_images/splash/sc_weekly_star_bg.png`
|
||||||
|
- `sc_images/splash/sc_icon_weekly_star_rank_1.png`
|
||||||
|
- `sc_images/splash/sc_icon_weekly_star_rank_2.png`
|
||||||
|
- `sc_images/splash/sc_icon_weekly_star_rank_3.png`
|
||||||
|
- `sc_images/splash/sc_last_weekly_cp_bg.png`
|
||||||
|
- `sc_images/splash/sc_icon_last_weekly_cp_rank_1.png`
|
||||||
|
- `sc_images/splash/sc_icon_last_weekly_cp_rank_2.png`
|
||||||
|
- `sc_images/splash/sc_icon_last_weekly_cp_rank_3.png`
|
||||||
- `.gitignore`
|
- `.gitignore`
|
||||||
- `android/key.properties`
|
- `android/key.properties`
|
||||||
- `pubspec.yaml`
|
- `pubspec.yaml`
|
||||||
|
|||||||