礼物动画以及多语言翻译修复
This commit is contained in:
parent
ea99051267
commit
61c670b9ff
@ -1,4 +1,7 @@
|
|||||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dhttps.protocols=TLSv1.2 -Djdk.tls.client.protocols=TLSv1.2 -Djava.net.preferIPv4Stack=true
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dhttps.protocols=TLSv1.2 -Djdk.tls.client.protocols=TLSv1.2 -Djava.net.preferIPv4Stack=true
|
||||||
|
org.gradle.daemon=true
|
||||||
|
org.gradle.parallel=true
|
||||||
|
org.gradle.caching=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
systemProp.https.protocols=TLSv1.2
|
systemProp.https.protocols=TLSv1.2
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"signInWithGoogle": "سيج إنويسجوغل",
|
"signInWithGoogle": "تسجيل الدخول باستخدام جوجل",
|
||||||
"or": "اور",
|
"or": "أو",
|
||||||
"signInWithYourAccount": "سيج ، إن ، فايس ، ويوا كونتي",
|
"signInWithYourAccount": "تسجيل الدخول باستخدام حسابك",
|
||||||
"signInWithApple": "سيج إنويس أبل",
|
"signInWithApple": "تسجيل الدخول باستخدام آبل",
|
||||||
"loginRepresentsAgreementTo": "تسجيل الدخول يمثل الموافقة على",
|
"loginRepresentsAgreementTo": "يعني تسجيل الدخول موافقتك على",
|
||||||
"termsofService": "تيمز بنسيفيتش",
|
"termsofService": "شروط الخدمة",
|
||||||
"privaceyPolicy": "بريفاسيبوليس",
|
"privaceyPolicy": "سياسة الخصوصية",
|
||||||
"tips": "تيبس",
|
"tips": "تنبيه",
|
||||||
"mine": "خاصتي",
|
"mine": "حسابي",
|
||||||
"searchNoDataTips": "أدخل معرف الغرفة أو المستخدم الذي تريد البحث عنه.",
|
"searchNoDataTips": "أدخل معرف الغرفة أو المستخدم الذي تريد البحث عنه.",
|
||||||
"party": "حفلة",
|
"party": "حفلة",
|
||||||
"event": "حدث",
|
"event": "حدث",
|
||||||
@ -15,16 +15,16 @@
|
|||||||
"sound2": "صوت",
|
"sound2": "صوت",
|
||||||
"games": "ألعاب",
|
"games": "ألعاب",
|
||||||
"myRoom": "غرفتي",
|
"myRoom": "غرفتي",
|
||||||
"other": "آخر",
|
"other": "أخرى",
|
||||||
"startYourBrandNewJourney": "ابدأ رحلتك الجديدة تمامًا",
|
"startYourBrandNewJourney": "ابدأ رحلتك الجديدة تمامًا",
|
||||||
"maliciousHarassment": "التحرش الخبيث",
|
"maliciousHarassment": "التحرش المسيء",
|
||||||
"aboutMe": "عنّي",
|
"aboutMe": "عنّي",
|
||||||
"bag": "حقيبة",
|
"bag": "حقيبة",
|
||||||
"roomEdit": "تحرير الغرفة",
|
"roomEdit": "تحرير الغرفة",
|
||||||
"cancelRoomPassword": "هل أنت متأكد أنك تريد حذف كلمة مرور الغرفة؟",
|
"cancelRoomPassword": "هل أنت متأكد أنك تريد حذف كلمة مرور الغرفة؟",
|
||||||
"roomMemberFee": "رسوم عضو الغرفة",
|
"roomMemberFee": "رسوم عضو الغرفة",
|
||||||
"blockedList2": "قائمة المحظورين",
|
"blockedList2": "قائمة المحظورين",
|
||||||
"roomTheme2": "موضوع الغرفة",
|
"roomTheme2": "سمة الغرفة",
|
||||||
"roomPassword": "كلمة مرور الغرفة",
|
"roomPassword": "كلمة مرور الغرفة",
|
||||||
"numberOfMic": "عدد الميكروفونات",
|
"numberOfMic": "عدد الميكروفونات",
|
||||||
"pleaseEnterContent": "يرجى إدخال المحتوى",
|
"pleaseEnterContent": "يرجى إدخال المحتوى",
|
||||||
@ -38,14 +38,14 @@
|
|||||||
"gameCenter": "مركز الألعاب",
|
"gameCenter": "مركز الألعاب",
|
||||||
"returnToVoiceChat": "العودة إلى الدردشة الصوتية؟",
|
"returnToVoiceChat": "العودة إلى الدردشة الصوتية؟",
|
||||||
"game": "لعبة",
|
"game": "لعبة",
|
||||||
"invite": "يدعو",
|
"invite": "دعوة",
|
||||||
"claim": "مطالبة",
|
"claim": "استلام",
|
||||||
"recent": "حديث",
|
"recent": "الأحدث",
|
||||||
"popularEvents": "الأحداث الشهيرة",
|
"popularEvents": "الأحداث الرائجة",
|
||||||
"exitGameMode": "الخروج من وضع اللعبة",
|
"exitGameMode": "الخروج من وضع اللعبة",
|
||||||
"enterTheRoom": "ادخل الغرفة",
|
"enterTheRoom": "دخول الغرفة",
|
||||||
"inviteGoRoomTips": "دائمًا هنا من أجلك، في المطر أو الشمس. تعال وقل مرحبًا!",
|
"inviteGoRoomTips": "دائمًا هنا من أجلك، في المطر أو الشمس. تعال وقل مرحبًا!",
|
||||||
"confirmInviteThisUserToTheRoom": "هل تؤكد دعوة هذا المستخدم (المعرف: {1}) إلى الغرفة؟",
|
"confirmInviteThisUserToTheRoom": "هل تؤكد دعوة هذا المستخدم (المعرّف: {1}) إلى الغرفة؟",
|
||||||
"complete": "مكتمل",
|
"complete": "مكتمل",
|
||||||
"copyLink": "نسخ الرابط",
|
"copyLink": "نسخ الرابط",
|
||||||
"shareTo": "مشاركة إلى",
|
"shareTo": "مشاركة إلى",
|
||||||
@ -55,9 +55,9 @@
|
|||||||
"taskNameRoomOwnerSendRedPacket": "مالك الغرفة يرسل مظروفًا أحمر",
|
"taskNameRoomOwnerSendRedPacket": "مالك الغرفة يرسل مظروفًا أحمر",
|
||||||
"taskNameRoomNewMember": "أعضاء جدد في الغرفة",
|
"taskNameRoomNewMember": "أعضاء جدد في الغرفة",
|
||||||
"taskNameRoomOwnerSendGiftUser": "مالك الغرفة يرسل هدية",
|
"taskNameRoomOwnerSendGiftUser": "مالك الغرفة يرسل هدية",
|
||||||
"taskNameRoomMicUser120Min": "الأعضاء بقضاء 120+ دقيقة على الميكروفون",
|
"taskNameRoomMicUser120Min": "أعضاء أمضوا 120+ دقيقة على الميكروفون",
|
||||||
"taskNameRoomMicUser60Min": "الأعضاء بقضاء 60+ دقيقة على الميكروفون",
|
"taskNameRoomMicUser60Min": "أعضاء أمضوا 60+ دقيقة على الميكروفون",
|
||||||
"taskNameRoomMicUser30Min": "الأعضاء بقضاء 30+ دقيقة على الميكروفون",
|
"taskNameRoomMicUser30Min": "أعضاء أمضوا 30+ دقيقة على الميكروفون",
|
||||||
"taskNamePersonalSendGift": "أرسل هدية لمستخدم",
|
"taskNamePersonalSendGift": "أرسل هدية لمستخدم",
|
||||||
"taskNameRoomOnlineUserCount": "الأعضاء المتصلون في الغرفة",
|
"taskNameRoomOnlineUserCount": "الأعضاء المتصلون في الغرفة",
|
||||||
"taskNameRoomOwnerInviteMic": "دعوة عضو إلى الميكروفون",
|
"taskNameRoomOwnerInviteMic": "دعوة عضو إلى الميكروفون",
|
||||||
@ -67,25 +67,25 @@
|
|||||||
"taskNamePersonalLuckyGiftGold": "العملات المكتسبة من إرسال هدايا الحظ",
|
"taskNamePersonalLuckyGiftGold": "العملات المكتسبة من إرسال هدايا الحظ",
|
||||||
"taskNameRoomOwnerMicTime": "مالك الغرفة يتحدث عبر الميكروفون في الغرفة",
|
"taskNameRoomOwnerMicTime": "مالك الغرفة يتحدث عبر الميكروفون في الغرفة",
|
||||||
"taskNamePersonalActiveInRoom": "كن نشطًا في غرف الآخرين",
|
"taskNamePersonalActiveInRoom": "كن نشطًا في غرف الآخرين",
|
||||||
"taskNamePersonalGameConsume": "إنفاق في اللعبة",
|
"taskNamePersonalGameConsume": "الإنفاق داخل الألعاب",
|
||||||
"taskNamePersonalMicInRoom": "اذهب إلى المايك",
|
"taskNamePersonalMicInRoom": "الصعود إلى الميكروفون",
|
||||||
"dailyCoinBonanzaRules": "قواعد مهرجان العملات اليومي",
|
"dailyCoinBonanzaRules": "قواعد مهرجان العملات اليومي",
|
||||||
"magic": "سحر",
|
"magic": "سحر",
|
||||||
"dailyCoinBonanzaRulesDetail": "1. يمكن إكمال المهام الشخصية اليومية ومهام صاحب الغرفة اليومية مرة واحدة لكل مستخدم يوميًا. يتم إعادة تعيين المهام عند الساعة 00:00:00 بتوقيت السعودية.\n2. يمكن إكمال المهام الشخصية اليومية فقط في غرف الآخرين؛ ويمكن إكمال مهام صاحب الغرفة فقط في غرفتك الخاصة.\n3. يتم احتساب مهام تقديم الهدايا فقط على الهدايا المرسلة في الغرف، وليس الهدايا المرسلة في الخلاصات.\n4. إذا تم إنشاء عدة حسابات باستخدام نفس الجهاز أو نفس بطاقة SIM بأي شكل من الأشكال، يمكن المطالبة بالمكافآت لجميع المهام مرة واحدة فقط.",
|
"dailyCoinBonanzaRulesDetail": "1. يمكن إكمال المهام الشخصية اليومية ومهام صاحب الغرفة اليومية مرة واحدة لكل مستخدم يوميًا. يتم إعادة تعيين المهام عند الساعة 00:00:00 بتوقيت السعودية.\n2. يمكن إكمال المهام الشخصية اليومية فقط في غرف الآخرين؛ ويمكن إكمال مهام صاحب الغرفة فقط في غرفتك الخاصة.\n3. يتم احتساب مهام تقديم الهدايا فقط على الهدايا المرسلة في الغرف، وليس الهدايا المرسلة في الخلاصات.\n4. إذا تم إنشاء عدة حسابات باستخدام نفس الجهاز أو نفس بطاقة SIM بأي شكل من الأشكال، يمكن المطالبة بالمكافآت لجميع المهام مرة واحدة فقط.",
|
||||||
"roomOwnerTasks": "مهام صاحب الغرفة",
|
"roomOwnerTasks": "مهام صاحب الغرفة",
|
||||||
"personalTasks": "المهام الشخصية",
|
"personalTasks": "المهام الشخصية",
|
||||||
"noPromptsToday": "لا توجد مطالبات اليوم.",
|
"noPromptsToday": "لا توجد مهام متاحة اليوم.",
|
||||||
"coins4": "عملات",
|
"coins4": "عملات",
|
||||||
"getPaidToRefer": "احصل على أجر عن الإحالة",
|
"getPaidToRefer": "اكسب من دعوة الأصدقاء",
|
||||||
"forMoreRewardsPleaseCheckTheTaskCenter": "لمزيد من المكافآت، يرجى التحقق من مركز المهام",
|
"forMoreRewardsPleaseCheckTheTaskCenter": "لمزيد من المكافآت، يرجى التحقق من مركز المهام",
|
||||||
"weekStart": "بداية الأسبوع",
|
"weekStart": "بداية الأسبوع",
|
||||||
"kingQuuen": "ملك-ملكة",
|
"kingQuuen": "الملك والملكة",
|
||||||
"ramadan": "رمضان",
|
"ramadan": "رمضان",
|
||||||
"like": "مثل",
|
"like": "إعجاب",
|
||||||
"updateNow": "تحديث الآن",
|
"updateNow": "تحديث الآن",
|
||||||
"skip2": "تخطي",
|
"skip2": "تخطي",
|
||||||
"importantReminder": "تذكير مهم",
|
"importantReminder": "تذكير مهم",
|
||||||
"discard": "تخلص",
|
"discard": "تجاهل",
|
||||||
"deleteCommentTips": "هل أنت متأكد أنك تريد حذف هذا التعليق؟",
|
"deleteCommentTips": "هل أنت متأكد أنك تريد حذف هذا التعليق؟",
|
||||||
"deleteSuccessful": "تم الحذف بنجاح!",
|
"deleteSuccessful": "تم الحذف بنجاح!",
|
||||||
"itemsLeft": "الأصناف المتبقية",
|
"itemsLeft": "الأصناف المتبقية",
|
||||||
@ -101,17 +101,17 @@
|
|||||||
"catchFirstComment": "التقط التعليق الأول",
|
"catchFirstComment": "التقط التعليق الأول",
|
||||||
"posting": "نشر",
|
"posting": "نشر",
|
||||||
"trend": "اتجاه",
|
"trend": "اتجاه",
|
||||||
"reply": "إجابة",
|
"reply": "رد",
|
||||||
"customizedGiftRules": "قواعد الهدايا المخصصة",
|
"customizedGiftRules": "قواعد الهدايا المخصصة",
|
||||||
"rulesUpload": "القواعد والرفع",
|
"rulesUpload": "القواعد والرفع",
|
||||||
"customized": "مخصص",
|
"customized": "مخصص",
|
||||||
"coins3": "عملات",
|
"coins3": "عملات",
|
||||||
"customizedGiftRulesContent": "كيفية الحصول على هديتي المُخصصة:\n\n1. يتم تحديد أهلية المستخدم للحصول على هدية مُخصصة بناءً على مستوى ثروته الحالي.\n\n(1) إذا كان مستوى ثروة المستخدم ≥ 35: يمكن للمستخدم تحميل فيديو واحد فقط للهدية المُخصصة. سنستخدم صورة ملفه الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير على الهدية في قسم \"التخصيص\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفر الهدية في المتجر.\n\n(2) إذا كان مستوى ثروة المستخدم ≥ 45: يمكن للمستخدم تحميل فيديو واحد فقط للهدية المُخصصة. سنستخدم صورة ملفه الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير على الهدية في قسم \"التخصيص\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفر الهدية في المتجر.\n\n2. يمكنك المشاركة في أنشطة مُحددة على التطبيق، وبعد استيفاء الشروط، تواصل معنا عبر قسم \"الرسائل\" ← \"اتصل بنا\". يرجى تزويدنا بلقطات شاشة للنشاط والفيديو الذي ترغب باستخدامه في الهدية المُخصصة. سنستخدم صورة ملفك الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير لها، ثم نُدرجها ضمن قسم \"مُخصصة\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفرها في المتجر.\n\nمدة صلاحية الهدايا المُخصصة وكيفية تمديدها:\nستكون الهدايا المُخصصة الحصرية متاحة لمدة 30 يومًا. ولتمكين المستخدمين المؤثرين والمُلهمين من الاستمرار في الاستمتاع بهذه التجربة الجديدة وعرض أسلوبهم الشخصي، يُمكن لمستخدمي الهدايا المُخصصة تمديد مدة صلاحية جميع هداياهم المُخصصة لمدة 30 يومًا إضافية عن طريق شحن رصيدهم بمبلغ 500 دولار أمريكي شهريًا.",
|
"customizedGiftRulesContent": "كيفية الحصول على هديتي المُخصصة:\n\n1. يتم تحديد أهلية المستخدم للحصول على هدية مُخصصة بناءً على مستوى ثروته الحالي.\n\n(1) إذا كان مستوى ثروة المستخدم ≥ 35: يمكن للمستخدم تحميل فيديو واحد فقط للهدية المُخصصة. سنستخدم صورة ملفه الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير على الهدية في قسم \"التخصيص\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفر الهدية في المتجر.\n\n(2) إذا كان مستوى ثروة المستخدم ≥ 45: يمكن للمستخدم تحميل فيديو واحد فقط للهدية المُخصصة. سنستخدم صورة ملفه الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير على الهدية في قسم \"التخصيص\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفر الهدية في المتجر.\n\n2. يمكنك المشاركة في أنشطة مُحددة على التطبيق، وبعد استيفاء الشروط، تواصل معنا عبر قسم \"الرسائل\" ← \"اتصل بنا\". يرجى تزويدنا بلقطات شاشة للنشاط والفيديو الذي ترغب باستخدامه في الهدية المُخصصة. سنستخدم صورة ملفك الشخصي الحالية كصورة للهدية، والفيديو المُقدم كتأثير لها، ثم نُدرجها ضمن قسم \"مُخصصة\". سيستغرق الإنتاج بعض الوقت، وسنُعلمك فور توفرها في المتجر.\n\nمدة صلاحية الهدايا المُخصصة وكيفية تمديدها:\nستكون الهدايا المُخصصة الحصرية متاحة لمدة 30 يومًا. ولتمكين المستخدمين المؤثرين والمُلهمين من الاستمرار في الاستمتاع بهذه التجربة الجديدة وعرض أسلوبهم الشخصي، يُمكن لمستخدمي الهدايا المُخصصة تمديد مدة صلاحية جميع هداياهم المُخصصة لمدة 30 يومًا إضافية عن طريق شحن رصيدهم بمبلغ 500 دولار أمريكي شهريًا.",
|
||||||
"clearCache": "مسح الكاش",
|
"clearCache": "مسح ذاكرة التخزين المؤقت",
|
||||||
"multiple": "متعدد",
|
"multiple": "متعدد",
|
||||||
"areYouSureYouWantToClearLocalCache": "هل أنت متأكد أنك تريد مسح الذاكرة المؤقتة المحلية؟",
|
"areYouSureYouWantToClearLocalCache": "هل أنت متأكد أنك تريد مسح الذاكرة المؤقتة المحلية؟",
|
||||||
"luckGiftSpecialEffects": "تأثيرات الرسوم المتحركة للهدية السعيدة",
|
"luckGiftSpecialEffects": "تأثيرات هدايا الحظ",
|
||||||
"receivedFromALuckyGift": "تم الاستلام من هدية محظوظة/سحرية",
|
"receivedFromALuckyGift": "تم الاستلام من هدية حظ/سحر",
|
||||||
"successfullyRemovedFromTheBlacklist": "تمت الإزالة من القائمة السوداء بنجاح!",
|
"successfullyRemovedFromTheBlacklist": "تمت الإزالة من القائمة السوداء بنجاح!",
|
||||||
"successfullyAddedToTheBlacklist": "تمت الإضافة إلى القائمة السوداء بنجاح!",
|
"successfullyAddedToTheBlacklist": "تمت الإضافة إلى القائمة السوداء بنجاح!",
|
||||||
"youAreCurrentlyCPRelationshipPleaseDissolve": "أنت حاليا في علاقة CP. الرجاء حلها أولا.",
|
"youAreCurrentlyCPRelationshipPleaseDissolve": "أنت حاليا في علاقة CP. الرجاء حلها أولا.",
|
||||||
@ -122,15 +122,15 @@
|
|||||||
"userBlacklist": "قائمة الحظر للمستخدمين",
|
"userBlacklist": "قائمة الحظر للمستخدمين",
|
||||||
"redEnvelopeNotYetClaimed": "المظروف الأحمر لم يُستلم بعد",
|
"redEnvelopeNotYetClaimed": "المظروف الأحمر لم يُستلم بعد",
|
||||||
"redEnvelopeAmount": "مبلغ الظرف الأحمر: {1} عملات",
|
"redEnvelopeAmount": "مبلغ الظرف الأحمر: {1} عملات",
|
||||||
"followed": "تبع",
|
"followed": "تمت المتابعة",
|
||||||
"newMessage": "رسالة جديدة",
|
"newMessage": "رسالة جديدة",
|
||||||
"keep": "يحفظ",
|
"keep": "الاحتفاظ",
|
||||||
"open": "فتح",
|
"open": "فتح",
|
||||||
"clearCacheSuccessfully": "تم مسح ذاكرة التخزين المؤقت بنجاح",
|
"clearCacheSuccessfully": "تم مسح ذاكرة التخزين المؤقت بنجاح",
|
||||||
"thisUserHasBeenBlacklisted": "تم إدراج هذا المستخدم في القائمة السوداء.",
|
"thisUserHasBeenBlacklisted": "تم إدراج هذا المستخدم في القائمة السوداء.",
|
||||||
"thisFeatureIsCurrentlyUnavailable": "هذه الخاصية غير متوفرة حاليا.",
|
"thisFeatureIsCurrentlyUnavailable": "هذه الميزة غير متاحة حاليًا.",
|
||||||
"pleaseSelectTheRecipient": "المرجو اختيار المستلم",
|
"pleaseSelectTheRecipient": "يرجى اختيار المستلم.",
|
||||||
"searchMemberIdHint": "يرجى إدخال رقم هوية العضو",
|
"searchMemberIdHint": "يرجى إدخال معرّف العضو",
|
||||||
"unclaimedRedEnvelopes": "الأظرفة الحمراء غير المطالب بها يتم استردادها خلال 24 ساعة.",
|
"unclaimedRedEnvelopes": "الأظرفة الحمراء غير المطالب بها يتم استردادها خلال 24 ساعة.",
|
||||||
"sentARedEnvelope": "أرسل ظرف أحمر.",
|
"sentARedEnvelope": "أرسل ظرف أحمر.",
|
||||||
"theRedEnvelopeHasExpired": "الظرف الأحمر منتهي الصلاحية",
|
"theRedEnvelopeHasExpired": "الظرف الأحمر منتهي الصلاحية",
|
||||||
@ -145,7 +145,7 @@
|
|||||||
"systemAnnouncementTips1": "احذر الاحتيال:",
|
"systemAnnouncementTips1": "احذر الاحتيال:",
|
||||||
"systemAnnouncementTips": "تحقق من المعلومات عن طريق القنوات الرسمية فقط. لا تقم بتنزيل برامج من طرف ثالث، ولا تشارك البيانات الشخصية، ولا تحول المال بناءً على طلبات خارجية. بطاقات هوية الموظفين الرسمية هي فقط 10000 و10003 و10086. في حالة أي شك، توقف وقدم التقرير عبر",
|
"systemAnnouncementTips": "تحقق من المعلومات عن طريق القنوات الرسمية فقط. لا تقم بتنزيل برامج من طرف ثالث، ولا تشارك البيانات الشخصية، ولا تحول المال بناءً على طلبات خارجية. بطاقات هوية الموظفين الرسمية هي فقط 10000 و10003 و10086. في حالة أي شك، توقف وقدم التقرير عبر",
|
||||||
"systemAnnouncement": "إعلان النظام",
|
"systemAnnouncement": "إعلان النظام",
|
||||||
"doNotClickUnfamiliarTips": "ماتضغطش على الروابط الغير معروفة، حيث قد تكشف معلوماتك الشخصية. متشاركش أبدا بطاقة الهوية أو معلومات البطاقة البنكية ديالك مع أي واحد.",
|
"doNotClickUnfamiliarTips": "لا تضغط على الروابط غير المألوفة، فقد تكشف معلوماتك الشخصية. لا تشارك رقم هويتك أو بيانات بطاقتك البنكية مع أي شخص.",
|
||||||
"atTag": "@تاغ",
|
"atTag": "@تاغ",
|
||||||
"sayHi2": "قل مرحبا",
|
"sayHi2": "قل مرحبا",
|
||||||
"roomBottomGreeting": "مرحبًا...",
|
"roomBottomGreeting": "مرحبًا...",
|
||||||
@ -153,33 +153,33 @@
|
|||||||
"reapply": "أعد التقديم",
|
"reapply": "أعد التقديم",
|
||||||
"cancelRequest": "إلغاء الطلب",
|
"cancelRequest": "إلغاء الطلب",
|
||||||
"supporter": "مشجع",
|
"supporter": "مشجع",
|
||||||
"numberOfSign": "عدد العلامة: {1}",
|
"numberOfSign": "عدد مرات التوقيع: {1}",
|
||||||
"hostWeeklyRank": "تصنيف المضيف الأسبوعي",
|
"hostWeeklyRank": "تصنيف المضيف الأسبوعي",
|
||||||
"supporterWeeklyRank": "ترتيب الأسبوعي للمشجعين",
|
"supporterWeeklyRank": "تصنيف الداعمين الأسبوعي",
|
||||||
"memberList": "قائمة الأعضاء",
|
"memberList": "قائمة الأعضاء",
|
||||||
"treasureChest": "صندوق الكنز",
|
"treasureChest": "صندوق الكنز",
|
||||||
"applicationRecord": "سجل الطلبات",
|
"applicationRecord": "سجل الطلبات",
|
||||||
"pending": "قيد الانتظار",
|
"pending": "قيد الانتظار",
|
||||||
"appUpdateTip": "التطبيق لديه نسخة جديدة ({1})، هل تود الذهاب وتحميلها؟",
|
"appUpdateTip": "يتوفر إصدار جديد من التطبيق ({1})، هل تريد تنزيله الآن؟",
|
||||||
"reconcile": "يصالح",
|
"reconcile": "مصالحة",
|
||||||
"separated": "منفصل",
|
"separated": "منفصل",
|
||||||
"areYouSureYouWantToSpend5": "{1} اعترف بالمشاعر ليك؛ إذا قبلتي، غادي توليو ثنائي.",
|
"areYouSureYouWantToSpend5": "{1} اعترف لك بمشاعره؛ إذا وافقت، ستصبحان ثنائيًا.",
|
||||||
"areYouSureYouWantToSpend6": "{1} يريد العودة للتقارب معك. إذا قررت المصالحة، سيتم استعادة جميع بياناتك السابقة.",
|
"areYouSureYouWantToSpend6": "{1} يريد العودة إليك. إذا قررت المصالحة، فستتم استعادة جميع بياناتكما السابقة.",
|
||||||
"reconcileInvitationTips": "إذا الطرف الآخر رفض دعوة CP، غتترجع العملات ديالك للمحفظة ديالك.",
|
"reconcileInvitationTips": "*إذا رفض الطرف الآخر دعوة CP، فستُعاد عملاتك إلى محفظتك.",
|
||||||
"reconcileInvitation": "دعوة للمصالحة",
|
"reconcileInvitation": "دعوة للمصالحة",
|
||||||
"areYouSureYouWantToSpend4": "إرسال دعوة للمصالحة لهدا المستخدم؟",
|
"areYouSureYouWantToSpend4": "هل تريد إرسال دعوة مصالحة إلى هذا المستخدم؟",
|
||||||
"partWaysTips": "إذا قرر أحد الشريكين في العلاقة الانفصال، سيكون هناك فترة تهدئة لمدة 7 أيام. خلال هذه الفترة، يمكن للطرفين اختيار المصالحة، وسيتم استعادة كل البيانات عند المصالحة. إذا لم يتم اختيار المصالحة بنهاية فترة السبعة أيام، سيتم مسح بيانات الزوجين.",
|
"partWaysTips": "إذا قرر أحد الشريكين في العلاقة الانفصال، سيكون هناك فترة تهدئة لمدة 7 أيام. خلال هذه الفترة، يمكن للطرفين اختيار المصالحة، وسيتم استعادة كل البيانات عند المصالحة. إذا لم يتم اختيار المصالحة بنهاية فترة السبعة أيام، سيتم مسح بيانات الزوجين.",
|
||||||
"areYouSureYouWantToPartWaysWithYourCP": "هل أنت متأكد أنك تريد الانفصال عن شريكك؟",
|
"areYouSureYouWantToPartWaysWithYourCP": "هل أنت متأكد أنك تريد الانفصال عن شريكك؟",
|
||||||
"partWays": "افترقوا",
|
"partWays": "الانفصال",
|
||||||
"firstDay": "اليوم الأول:{1}",
|
"firstDay": "اليوم الأول:{1}",
|
||||||
"timeSpentTogether": "الوقت المشترك: {1} أيام",
|
"timeSpentTogether": "الوقت المشترك: {1} أيام",
|
||||||
"areYouSureYouWantToSpend3": "إذا الطرف الآخر رفض دعوة CP، غتترجع العملات ديالك للمحفظة ديالك.",
|
"areYouSureYouWantToSpend3": "*إذا رفض الطرف الآخر دعوة CP، فستُعاد عملاتك إلى محفظتك.",
|
||||||
"areYouSureYouWantToSpend2": "هل تريد إرسال دعوة CP لهذا المستخدم؟",
|
"areYouSureYouWantToSpend2": "هل تريد إرسال دعوة CP لهذا المستخدم؟",
|
||||||
"areYouSureYouWantToSpend": "هل أنت متأكد أنك تريد أن تنفق",
|
"areYouSureYouWantToSpend": "هل أنت متأكد أنك تريد أن تنفق",
|
||||||
"underReview": "قيد المراجعة",
|
"underReview": "قيد المراجعة",
|
||||||
"doYouWantToDeleteIt": "هل تريد حذفه؟",
|
"doYouWantToDeleteIt": "هل تريد حذفه؟",
|
||||||
"myPhoto": "صورتي",
|
"myPhoto": "صورتي",
|
||||||
"balanceNotEnough": "رصيد العملات الذهبية غير كافي. واش بغيت تزود الرصيد؟",
|
"balanceNotEnough": "رصيد العملات الذهبية غير كافٍ. هل تريد إعادة الشحن؟",
|
||||||
"skip": "تخطي {1}",
|
"skip": "تخطي {1}",
|
||||||
"chooseFromAblum": "اختر من الألبوم",
|
"chooseFromAblum": "اختر من الألبوم",
|
||||||
"information": "معلومات",
|
"information": "معلومات",
|
||||||
@ -187,16 +187,16 @@
|
|||||||
"editProfile": "تعديل الملف الشخصي",
|
"editProfile": "تعديل الملف الشخصي",
|
||||||
"sendTheCpRequest": "أرسل طلب CP",
|
"sendTheCpRequest": "أرسل طلب CP",
|
||||||
"addCp": "أضف CP",
|
"addCp": "أضف CP",
|
||||||
"numberOfMyCPs": "عدد نقاط السيطرة ديالي:({1}/{2})",
|
"numberOfMyCPs": "عدد علاقات CP الخاصة بي:({1}/{2})",
|
||||||
"relationShip": "علاقة",
|
"relationShip": "علاقة",
|
||||||
"props": "الدعائم",
|
"props": "الدعائم",
|
||||||
"couple2": "زوج",
|
"couple2": "ثنائي",
|
||||||
"dice": "نرد",
|
"dice": "نرد",
|
||||||
"operationsAreTooFrequent": "العمليات متكررة بشكل مفرط",
|
"operationsAreTooFrequent": "العمليات متكررة بشكل مفرط",
|
||||||
"luckNumber": "رقم الحظ",
|
"luckNumber": "رقم الحظ",
|
||||||
"rps": "حجر ورقة مقص",
|
"rps": "حجر ورقة مقص",
|
||||||
"couple": "زوج {1}:",
|
"couple": "زوج {1}:",
|
||||||
"noMatchedCP": "لا يوجد CP مطابق",
|
"noMatchedCP": "لا توجد علاقة CP مطابقة",
|
||||||
"adminInviteRechargeAgent": "ندعوك لتصبح وكيلا لإعادة الشحن.",
|
"adminInviteRechargeAgent": "ندعوك لتصبح وكيلا لإعادة الشحن.",
|
||||||
"ownerIncomeCoins": "دخل المالك: {1} عملة",
|
"ownerIncomeCoins": "دخل المالك: {1} عملة",
|
||||||
"allGames": "كل الألعاب",
|
"allGames": "كل الألعاب",
|
||||||
@ -207,9 +207,9 @@
|
|||||||
"greedyClass": "الفئة الجشعة",
|
"greedyClass": "الفئة الجشعة",
|
||||||
"hotGames": "الألعاب الرائجة",
|
"hotGames": "الألعاب الرائجة",
|
||||||
"sent": "أُرسل",
|
"sent": "أُرسل",
|
||||||
"termsOfServicePrivacyPolicyTips": "بمتابعتك، أنت توافق على شروط الخدمة و سياسة الخصوصية",
|
"termsOfServicePrivacyPolicyTips": "بالمتابعة، فإنك توافق على شروط الخدمة وسياسة الخصوصية",
|
||||||
"and": " اندر ",
|
"and": " و ",
|
||||||
"chatBox": "صندوق الشات",
|
"chatBox": "صندوق الدردشة",
|
||||||
"confirm": "تأكيد",
|
"confirm": "تأكيد",
|
||||||
"cancel": "إلغاء",
|
"cancel": "إلغاء",
|
||||||
"join": "انضم",
|
"join": "انضم",
|
||||||
@ -217,23 +217,23 @@
|
|||||||
"fans": "المعجبون",
|
"fans": "المعجبون",
|
||||||
"items": "عناصر",
|
"items": "عناصر",
|
||||||
"letGoToWatch": "هيا نذهب للمشاهدة!",
|
"letGoToWatch": "هيا نذهب للمشاهدة!",
|
||||||
"launchedARocket": "أطلق صاروخ",
|
"launchedARocket": "أطلق صاروخًا",
|
||||||
"sendUserId": "أرسل معرف المستخدم:{1}",
|
"sendUserId": "أرسل معرف المستخدم:{1}",
|
||||||
"giveUpIdentity": "تخلى عن الهوية",
|
"giveUpIdentity": "تخلى عن الهوية",
|
||||||
"leaveRoomIdentityTips": "هل أنت متأكد من أنك تريد التخلي عن هوية الغرفة؟",
|
"leaveRoomIdentityTips": "هل أنت متأكد من أنك تريد التخلي عن هوية الغرفة؟",
|
||||||
"joinMemberTips2": "هل تريد تأكيد انضمامك إلى الغرفة كعضو؟",
|
"joinMemberTips2": "هل تريد تأكيد انضمامك إلى الغرفة كعضو؟",
|
||||||
"sureUnfollowThisRoom": "متأكد بغيتي تحيد المتابعة لهاذ الغرفة؟",
|
"sureUnfollowThisRoom": "هل أنت متأكد من إلغاء متابعة هذه الغرفة؟",
|
||||||
"settings": "الإعدادات",
|
"settings": "الإعدادات",
|
||||||
"alreadyAnTourist": "أنت بالفعل سائح",
|
"alreadyAnTourist": "أنت بالفعل سائح",
|
||||||
"deleteConversationTips": "هل أنت متأكد أنك تريد حذف سجل المحادثة مع هذا المستخدم؟",
|
"deleteConversationTips": "هل أنت متأكد أنك تريد حذف سجل المحادثة مع هذا المستخدم؟",
|
||||||
"account": "أكونتي",
|
"account": "الحساب",
|
||||||
"youHaventFollowed": "أنت لم تتابع أي غرفة",
|
"youHaventFollowed": "أنت لم تتابع أي غرفة",
|
||||||
"messageHasBeenRecalled": "تم استرجاع هذه الرسالة",
|
"messageHasBeenRecalled": "تم استرجاع هذه الرسالة",
|
||||||
"propMessagePrompt": "موجه رسالة دعائية",
|
"propMessagePrompt": "موجه رسالة دعائية",
|
||||||
"deleteFromMyDevice": "احذف من جهازي",
|
"deleteFromMyDevice": "احذف من جهازي",
|
||||||
"deleteOnAllDevices": "حذف على جميع الأجهزة",
|
"deleteOnAllDevices": "حذف على جميع الأجهزة",
|
||||||
"inputUserId": "أدخل معرف المستخدم",
|
"inputUserId": "أدخل معرف المستخدم",
|
||||||
"common": "شائع",
|
"common": "عام",
|
||||||
"useCoupontips": "هل أنت متأكد أنك تريد استخدام القسيمة؟",
|
"useCoupontips": "هل أنت متأكد أنك تريد استخدام القسيمة؟",
|
||||||
"searchUserId": "ابحث عن معرف المستخدم",
|
"searchUserId": "ابحث عن معرف المستخدم",
|
||||||
"receiveSucc": "تم المطالبة بنجاح",
|
"receiveSucc": "تم المطالبة بنجاح",
|
||||||
@ -242,22 +242,22 @@
|
|||||||
"bio": "معلومات شخصية",
|
"bio": "معلومات شخصية",
|
||||||
"delete": "حذف",
|
"delete": "حذف",
|
||||||
"sendCoupontips": "هل أنت متأكد أنك تريد إرسال هذه القسيمة لهذا المستخدم؟",
|
"sendCoupontips": "هل أنت متأكد أنك تريد إرسال هذه القسيمة لهذا المستخدم؟",
|
||||||
"recallThisMessage": "تذكر هذه الرسالة؟",
|
"recallThisMessage": "هل تريد استرجاع هذه الرسالة؟",
|
||||||
"copy": "نسخ",
|
"copy": "نسخ",
|
||||||
"youDontHaveAnyCouponsYet": "ليس لديك أي كوبونات بعد",
|
"youDontHaveAnyCouponsYet": "ليس لديك أي كوبونات بعد",
|
||||||
"hobby": "هواية",
|
"hobby": "هواية",
|
||||||
"recall": "استدعاء",
|
"recall": "استرجاع",
|
||||||
"accept": "اقبل",
|
"accept": "قبول",
|
||||||
"signedin": "تم التوقيع",
|
"signedin": "تم تسجيل الدخول",
|
||||||
"following": "التالي",
|
"following": "يتابع",
|
||||||
"inviteYouToBecomeBD": "ندعوك لتصبح BD",
|
"inviteYouToBecomeBD": "ندعوك للانضمام كـ BD.",
|
||||||
"confirmAcceptTheInvitation": "هل تريد تأكيد قبول الدعوة؟",
|
"confirmAcceptTheInvitation": "هل تريد تأكيد قبول الدعوة؟",
|
||||||
"confirmDeclineTheInvitation": "تأكيد رفض الدعوة؟",
|
"confirmDeclineTheInvitation": "تأكيد رفض الدعوة؟",
|
||||||
"friends": "أصدقاء",
|
"friends": "أصدقاء",
|
||||||
"language": "اللغة",
|
"language": "اللغة",
|
||||||
"feedback": "تعليق",
|
"feedback": "ملاحظات",
|
||||||
"coupon": "قسيمة",
|
"coupon": "قسيمة",
|
||||||
"get": "احصل",
|
"get": "احصل على",
|
||||||
"couponRecord": "سجل استخدام القسيمة",
|
"couponRecord": "سجل استخدام القسيمة",
|
||||||
"inRoom": "في الغرفة",
|
"inRoom": "في الغرفة",
|
||||||
"inRocket": "في الصاروخ",
|
"inRocket": "في الصاروخ",
|
||||||
@ -265,21 +265,21 @@
|
|||||||
"searchCouponHint": "ابحث عن كوبون",
|
"searchCouponHint": "ابحث عن كوبون",
|
||||||
"search": "بحث",
|
"search": "بحث",
|
||||||
"checkInSuccessful": "تسجيل الوصول ناجح",
|
"checkInSuccessful": "تسجيل الوصول ناجح",
|
||||||
"about": "حوالي",
|
"about": "حول",
|
||||||
"inviteYouToBecomeHost": "ندعوك لتصبح مضيفًا",
|
"inviteYouToBecomeHost": "ندعوك لتصبح مضيفًا",
|
||||||
"receive": "استقبل",
|
"receive": "استلام",
|
||||||
"sginTips": "ستحصل على المكافأة في أول مرة تسجل دخولك فيها كل يوم. إذا قمت بقطع تسجيل دخولك، ستُحسب المكافأة من أول يوم تسجل فيه دخولك مرة أخرى.",
|
"sginTips": "ستحصل على المكافأة في أول مرة تسجل دخولك فيها كل يوم. إذا قمت بقطع تسجيل دخولك، ستُحسب المكافأة من أول يوم تسجل فيه دخولك مرة أخرى.",
|
||||||
"aboutUs": "حولنا",
|
"aboutUs": "من نحن",
|
||||||
"theme": "موضوع",
|
"theme": "السمة",
|
||||||
"home": "المنزل",
|
"home": "الرئيسية",
|
||||||
"luck": "حظ",
|
"luck": "حظ",
|
||||||
"bDLeaderInviteYouToBecomeBDLeader": "ندعوك لتصبح قائد تطوير الأعمال",
|
"bDLeaderInviteYouToBecomeBDLeader": "ندعوك لتصبح قائد تطوير الأعمال",
|
||||||
"win2": "فوز {1}",
|
"win2": "فوز {1}",
|
||||||
"level": "مستوى",
|
"level": "مستوى",
|
||||||
"wealthLevel": "مستوى الثروة",
|
"wealthLevel": "مستوى الثروة",
|
||||||
"userLevel": "مستوى المستخدم",
|
"userLevel": "مستوى المستخدم",
|
||||||
"themeGoToUploadTips": "1.سيتم مراجعة التحميل خلال 24 ساعة بعد نجاحه. 2.\n سيتم إرجاع جميع العملات إذا فشلت المراجعة.",
|
"themeGoToUploadTips": "1. ستتم مراجعة التحميل خلال 24 ساعة بعد نجاحه.\n2. ستُعاد جميع العملات إذا فشلت المراجعة.",
|
||||||
"goToUpload": "اذهب لتحميل",
|
"goToUpload": "الانتقال للرفع",
|
||||||
"rechargeAgency": "وكالة الشحن",
|
"rechargeAgency": "وكالة الشحن",
|
||||||
"logout": "تسجيل الخروج",
|
"logout": "تسجيل الخروج",
|
||||||
"adminCenter": "مركز الإدارة",
|
"adminCenter": "مركز الإدارة",
|
||||||
@ -289,7 +289,7 @@
|
|||||||
"fansList": "قائمة المعجبين",
|
"fansList": "قائمة المعجبين",
|
||||||
"pleaseSelectTheTypeContent": "يرجى اختيار نوع المحتوى المخالف",
|
"pleaseSelectTheTypeContent": "يرجى اختيار نوع المحتوى المخالف",
|
||||||
"me": "أنا",
|
"me": "أنا",
|
||||||
"socialPrivilege": "امتياز اجتماعي",
|
"socialPrivilege": "امتيازات اجتماعية",
|
||||||
"spam": "بريد مزعج",
|
"spam": "بريد مزعج",
|
||||||
"inappropriateContent": "محتوى غير لائق",
|
"inappropriateContent": "محتوى غير لائق",
|
||||||
"illegalInformation": "معلومات غير قانونية",
|
"illegalInformation": "معلومات غير قانونية",
|
||||||
@ -297,11 +297,11 @@
|
|||||||
"identity": "الهوية",
|
"identity": "الهوية",
|
||||||
"adjust": "تعديل",
|
"adjust": "تعديل",
|
||||||
"roomReward": "مكافأة الغرفة",
|
"roomReward": "مكافأة الغرفة",
|
||||||
"insufhcientGoldsGoToRecharge": "الذهب غير كافي، عاود الشحن دابا!",
|
"insufhcientGoldsGoToRecharge": "الرصيد غير كافٍ، يرجى إعادة الشحن الآن!",
|
||||||
"received": "تم الاستلام",
|
"received": "تم الاستلام",
|
||||||
"goToRecharge": "اذهب لإعادة الشحن",
|
"goToRecharge": "اذهب لإعادة الشحن",
|
||||||
"warning": "تحذير",
|
"warning": "تحذير",
|
||||||
"ownerSendTheRedEnvelope": "المالك أرسل عملات المكافأة",
|
"ownerSendTheRedEnvelope": "أرسل مالك الغرفة عملات المكافأة.",
|
||||||
"rewardCoins": "عملات المكافأة:{1} عملة",
|
"rewardCoins": "عملات المكافأة:{1} عملة",
|
||||||
"lastWeekProgress": "تقدم الأسبوع الماضي",
|
"lastWeekProgress": "تقدم الأسبوع الماضي",
|
||||||
"currentProgress": "التقدم الحالي",
|
"currentProgress": "التقدم الحالي",
|
||||||
@ -313,7 +313,7 @@
|
|||||||
"remainingNumberTips": "العدد المتبقي المتاح: ({1}/{2})",
|
"remainingNumberTips": "العدد المتبقي المتاح: ({1}/{2})",
|
||||||
"collectionTimeTips": "وقت الجمع:{1}({2}/{3})",
|
"collectionTimeTips": "وقت الجمع:{1}({2}/{3})",
|
||||||
"sendARedEnvelope": "أرسل ظرفاً أحمر",
|
"sendARedEnvelope": "أرسل ظرفاً أحمر",
|
||||||
"sendRedPackConfirmTips": "هل أنت متأكد أنك تريد إرسال الحزمة الحمراء؟",
|
"sendRedPackConfirmTips": "هل أنت متأكد أنك تريد إرسال الظرف الأحمر؟",
|
||||||
"redEnvelopeSendingRecords": "سجلات إرسال الظرف الأحمر:",
|
"redEnvelopeSendingRecords": "سجلات إرسال الظرف الأحمر:",
|
||||||
"countdownMinutes": "دقائق العد التنازلي:",
|
"countdownMinutes": "دقائق العد التنازلي:",
|
||||||
"number2": "رقم:",
|
"number2": "رقم:",
|
||||||
@ -326,12 +326,12 @@
|
|||||||
"roomTools": "أدوات الغرفة:",
|
"roomTools": "أدوات الغرفة:",
|
||||||
"entertainment": "الترفيه:",
|
"entertainment": "الترفيه:",
|
||||||
"reportSucc": "تم الإبلاغ بنجاح",
|
"reportSucc": "تم الإبلاغ بنجاح",
|
||||||
"reportInputTips": "من فضلك وصف المشكل بأكبر قدر ممكن من التفاصيل باش نقدر نفهموه ونحلوه.",
|
"reportInputTips": "يرجى وصف المشكلة بأكبر قدر ممكن من التفاصيل حتى نتمكن من فهمها وحلها.",
|
||||||
"pornography": "الاباحية",
|
"pornography": "إباحية",
|
||||||
"screenshotTips": "لقطة شاشة (حتى 3)",
|
"screenshotTips": "لقطة شاشة (حتى 3)",
|
||||||
"roomEditing": "تحرير الغرفة",
|
"roomEditing": "تحرير الغرفة",
|
||||||
"picture": "صورة",
|
"picture": "صورة",
|
||||||
"inputDesHint": "من فضلك وصف المشكلة بأكبر قدر ممكن من التفاصيل باش نقدر نفهموها ونحلها.",
|
"inputDesHint": "يرجى وصف المشكلة بأكبر قدر ممكن من التفاصيل حتى نتمكن من فهمها وحلها.",
|
||||||
"description": "الوصف:",
|
"description": "الوصف:",
|
||||||
"roomNotice": "إشعار الغرفة",
|
"roomNotice": "إشعار الغرفة",
|
||||||
"roomTheme": "موضوع الغرفة",
|
"roomTheme": "موضوع الغرفة",
|
||||||
@ -343,25 +343,25 @@
|
|||||||
"enterTheUserId": "أدخل معرف المستخدم",
|
"enterTheUserId": "أدخل معرف المستخدم",
|
||||||
"enterTheRoomId": "أدخل رقم الغرفة",
|
"enterTheRoomId": "أدخل رقم الغرفة",
|
||||||
"theImageSizeCannotExceed": "حجم الصورة لا يمكن أن يتجاوز 4 ميغابايت",
|
"theImageSizeCannotExceed": "حجم الصورة لا يمكن أن يتجاوز 4 ميغابايت",
|
||||||
"bdLeader": "زعيم بنك التنمية",
|
"bdLeader": "قائد BD",
|
||||||
"kickedOutOfRoom": "تم طرده من الغرفة",
|
"kickedOutOfRoom": "تم طرده من الغرفة",
|
||||||
"lockTheMic": "اقفل الميكروفون",
|
"lockTheMic": "قفل الميكروفون",
|
||||||
"openTheMic": "افتح الميكروفون",
|
"openTheMic": "فتح الميكروفون",
|
||||||
"finish": "أنهى",
|
"finish": "إنهاء",
|
||||||
"removeTheMic": "أزل الميكروفون",
|
"removeTheMic": "أزل الميكروفون",
|
||||||
"leavelTheMic": "اترك الميكروفون",
|
"leavelTheMic": "اترك الميكروفون",
|
||||||
"unlockTheMic": "افتح الميكروفون",
|
"unlockTheMic": "إلغاء قفل الميكروفون",
|
||||||
"muteTheMic": "كتم الميكروفون",
|
"muteTheMic": "كتم الميكروفون",
|
||||||
"inviteToTheMicrophone": "دعوة للتحدث في الميكروفون",
|
"inviteToTheMicrophone": "دعوة للتحدث في الميكروفون",
|
||||||
"takeTheMic": "خذ الميكروفون",
|
"takeTheMic": "خذ الميكروفون",
|
||||||
"openUserProfleCard": "افتح بطاقة ملف المستخدم",
|
"openUserProfleCard": "افتح بطاقة ملف المستخدم",
|
||||||
"crop": "محصول",
|
"crop": "قص",
|
||||||
"host": "مضيف",
|
"host": "مضيف",
|
||||||
"agent": "وكالة",
|
"agent": "وكيل",
|
||||||
"ra": "را",
|
"ra": "RA",
|
||||||
"bd": "بي دي",
|
"bd": "BD",
|
||||||
"unread": "غير مقروء",
|
"unread": "غير مقروء",
|
||||||
"read": "اقرأ",
|
"read": "مقروء",
|
||||||
"image": "[صورة]",
|
"image": "[صورة]",
|
||||||
"video": "[فيديو]",
|
"video": "[فيديو]",
|
||||||
"sound": "[صوت]",
|
"sound": "[صوت]",
|
||||||
@ -371,13 +371,13 @@
|
|||||||
"giftCounter": "عداد الهدايا",
|
"giftCounter": "عداد الهدايا",
|
||||||
"wins": "يفوز",
|
"wins": "يفوز",
|
||||||
"deleteAccount": "حذف الحساب",
|
"deleteAccount": "حذف الحساب",
|
||||||
"becomeAgent": "أصبح وكيلا",
|
"becomeAgent": "كن وكيلاً",
|
||||||
"male": "ذكر",
|
"male": "ذكر",
|
||||||
"setAccount": "تعيين الحساب",
|
"setAccount": "إعداد الحساب",
|
||||||
"female": "أنثى",
|
"female": "أنثى",
|
||||||
"album": "ألبوم",
|
"album": "ألبوم",
|
||||||
"loadingFailedClickToRetry": "فشل التحميل، انقر لإعادة المحاولة",
|
"loadingFailedClickToRetry": "فشل التحميل، انقر لإعادة المحاولة",
|
||||||
"releaseToLoadMore": "حرر للتحميل المزيد",
|
"releaseToLoadMore": "حرر للتحميل",
|
||||||
"haveMyLimits": "---لدي حدودي.---",
|
"haveMyLimits": "---لدي حدودي.---",
|
||||||
"pullToLoadMore": "اسحب للتحميل المزيد",
|
"pullToLoadMore": "اسحب للتحميل المزيد",
|
||||||
"enterNickname": "أدخل الاسم المستعار",
|
"enterNickname": "أدخل الاسم المستعار",
|
||||||
@ -394,17 +394,17 @@
|
|||||||
"saturday": "السبت {1}",
|
"saturday": "السبت {1}",
|
||||||
"sunday": "الأحد {1}",
|
"sunday": "الأحد {1}",
|
||||||
"notifcation": "إشعار",
|
"notifcation": "إشعار",
|
||||||
"inviteYouToBecomeAgent": "ندعوك لتصبح وكالة",
|
"inviteYouToBecomeAgent": "ندعوك لتصبح وكيلاً.",
|
||||||
"theVideoSizeCannotExceed": "لا يمكن أن يتجاوز حجم الفيديو 50 ميغابايت",
|
"theVideoSizeCannotExceed": "لا يمكن أن يتجاوز حجم الفيديو 50 ميغابايت",
|
||||||
"fromLuckyGifts": "من الهدايا المحظوظة",
|
"fromLuckyGifts": "من هدايا الحظ/السحر",
|
||||||
"confirmSwitchMicModelTips": "هل تؤكد تغيير وضعية الجلوس؟",
|
"confirmSwitchMicModelTips": "هل تؤكد تغيير وضعية الجلوس؟",
|
||||||
"classicMic": "ميكروفون كلاسيكي {1}",
|
"classicMic": "ميكروفون كلاسيكي {1}",
|
||||||
"number": "رقم",
|
"number": "رقم",
|
||||||
"micTheme": "ثيم المايك",
|
"micTheme": "سمة الميكروفون",
|
||||||
"chat": "دردشة",
|
"chat": "دردشة",
|
||||||
"rejected": "مرفوض",
|
"rejected": "مرفوض",
|
||||||
"refuse": "رفض",
|
"refuse": "رفض",
|
||||||
"boxContributeTips": "لقد تم الاستثمار بالفعل اليوم، رجاءً لا تستثمر مرة أخرى",
|
"boxContributeTips": "تم الاستثمار اليوم بالفعل، يرجى عدم الاستثمار مرة أخرى.",
|
||||||
"help": "مساعدة",
|
"help": "مساعدة",
|
||||||
"approved": "معتمد",
|
"approved": "معتمد",
|
||||||
"onlineUsers": "المستخدمون عبر الإنترنت ({1}/{2}):",
|
"onlineUsers": "المستخدمون عبر الإنترنت ({1}/{2}):",
|
||||||
@ -418,14 +418,14 @@
|
|||||||
"joinRequest": "طلب الانضمام",
|
"joinRequest": "طلب الانضمام",
|
||||||
"gameRules": "قوانين اللعبة:",
|
"gameRules": "قوانين اللعبة:",
|
||||||
"gift": "هدية",
|
"gift": "هدية",
|
||||||
"charmGameRulesTips": "قم بتشغيل لوحة التحكم لعرض العملات الهدايا المستلمة لجميع المستخدمين على الميكروفون، 1 عملة = 1 نقطة (هدية محظوظة 1 عملة = 0.04 نقطة).",
|
"charmGameRulesTips": "شغّل لوحة التحكم لعرض عملات الهدايا المستلمة لجميع المستخدمين على الميكروفون؛ 1 عملة = 1 نقطة (هدية الحظ: 1 عملة = 0.04 نقطة).",
|
||||||
"charm": "تعويذة هدية",
|
"charm": "سحر الهدايا",
|
||||||
"chats": "الدردشات",
|
"chats": "الدردشات",
|
||||||
"myMusic": "موسيقاي",
|
"myMusic": "موسيقاي",
|
||||||
"membershipFeeTips1": "من فضلك حدد رسوم الانضمام لغرفتك. يمكن للمستخدمين الانضمام إلى غرفتك من خلال دفع الرسوم.",
|
"membershipFeeTips1": "من فضلك حدد رسوم الانضمام لغرفتك. يمكن للمستخدمين الانضمام إلى غرفتك من خلال دفع الرسوم.",
|
||||||
"membershipFeeTips2": "الذهب المطلوب للعضو في الغرفة. صاحب الغرفة سيحصل على 50٪ من الذهب.",
|
"membershipFeeTips2": "الذهب المطلوب للعضو في الغرفة. صاحب الغرفة سيحصل على 50٪ من الذهب.",
|
||||||
"membershipFee": "رسوم العضوية",
|
"membershipFee": "رسوم العضوية",
|
||||||
"touristsSendText": "السائح يرسل نص",
|
"touristsSendText": "إرسال النص للزوار",
|
||||||
"freePrice": "الرسوم: 0-10000",
|
"freePrice": "الرسوم: 0-10000",
|
||||||
"pleaseChatFfriendly": "رجاءً تحدث بطريقة ودية",
|
"pleaseChatFfriendly": "رجاءً تحدث بطريقة ودية",
|
||||||
"kickPrevention": "الوقاية من الركلات",
|
"kickPrevention": "الوقاية من الركلات",
|
||||||
@ -435,39 +435,39 @@
|
|||||||
"stop": "توقف",
|
"stop": "توقف",
|
||||||
"add": "أضف",
|
"add": "أضف",
|
||||||
"scrollToTheBottom": "قم بالتمرير إلى الأسفل",
|
"scrollToTheBottom": "قم بالتمرير إلى الأسفل",
|
||||||
"apple": "تفاحة",
|
"apple": "آبل",
|
||||||
"music": "موسيقى",
|
"music": "موسيقى",
|
||||||
"free": "مجاناً",
|
"free": "مجاني",
|
||||||
"goToUpgrade": "اذهب إلى الترقية",
|
"goToUpgrade": "الانتقال للترقية",
|
||||||
"google": "جوجل",
|
"google": "جوجل",
|
||||||
"exclusiveEmojiWillBeReleasedAfterBecoming": "سيتم إصدار الرموز التعبيرية الحصرية بعد أن تصبح",
|
"exclusiveEmojiWillBeReleasedAfterBecoming": "سيتم إصدار الرموز التعبيرية الحصرية بعد أن تصبح",
|
||||||
"preventBeingBlocked": "منع الحظر",
|
"preventBeingBlocked": "منع الحظر",
|
||||||
"enableRankIncognitoMode": "تمكين وضع الترتيب الخفي",
|
"enableRankIncognitoMode": "تمكين وضع الترتيب الخفي",
|
||||||
"avoidBeingKicked": "تجنب الركل",
|
"avoidBeingKicked": "تجنب الركل",
|
||||||
"privileges": "{1} الامتيازات",
|
"privileges": "امتيازات {1}",
|
||||||
"andAboveUsers": "{1} والمستخدمون أعلاه",
|
"andAboveUsers": "مستخدمو {1} فما فوق",
|
||||||
"everyone": "الجميع",
|
"everyone": "الجميع",
|
||||||
"basicPermissions": "الأذونات الأساسية",
|
"basicPermissions": "الأذونات الأساسية",
|
||||||
"mysteriousInvisibility": "الاختفاء الغامض",
|
"mysteriousInvisibility": "الاختفاء الغامض",
|
||||||
"antiBlock": "مضاد للانسداد",
|
"antiBlock": "مضاد للحظر",
|
||||||
"privateChat": "دردشة خاصة",
|
"privateChat": "دردشة خاصة",
|
||||||
"storeDiscount": "خصم المتجر {1}",
|
"storeDiscount": "خصم المتجر {1}",
|
||||||
"membershipFreeChatSpeak": "دردشة وتحدث بدون عضوية",
|
"membershipFreeChatSpeak": "دردشة وتحدث بدون عضوية",
|
||||||
"priorityRoomSorting": "فرز الغرف حسب الأولوية",
|
"priorityRoomSorting": "فرز الغرف حسب الأولوية",
|
||||||
"userColoredID": "معرّف المستخدم الملون",
|
"userColoredID": "معرّف المستخدم الملون",
|
||||||
"setLoginPassword": "حدد كلمة مرور تسجيل الدخول",
|
"setLoginPassword": "تعيين كلمة مرور تسجيل الدخول",
|
||||||
"theTwoPasswordsDoNotMatch": "كلمتا المرور لا تتطابقتان.",
|
"theTwoPasswordsDoNotMatch": "كلمتا المرور لا تتطابقتان.",
|
||||||
"resetLoginPasswordtTips2": "كلمة المرور يجب أن تكون بطول 8-16 حرف ويجب أن تكون مزيج من الأحرف الإنجليزية الكبيرة والصغيرة والأرقام (ليس فقط أرقام)",
|
"resetLoginPasswordtTips2": "يجب أن تتكون كلمة المرور من 8 إلى 16 حرفًا، وأن تجمع بين الأحرف الإنجليزية الكبيرة والصغيرة والأرقام (وليس الأرقام فقط).",
|
||||||
"confirmYourPassword": "أكد كلمة المرور الخاصة بك",
|
"confirmYourPassword": "أكد كلمة المرور الخاصة بك",
|
||||||
"enterYourNewPassword": "أدخل كلمة مرورك الجديدة",
|
"enterYourNewPassword": "أدخل كلمة مرورك الجديدة",
|
||||||
"setYourPassword": "عيّن كلمة المرور ديالك",
|
"setYourPassword": "عيّن كلمة المرور الخاصة بك",
|
||||||
"enterYourOldPassword": "أدخل كلمة السر القديمة الخاصة بك",
|
"enterYourOldPassword": "أدخل كلمة السر القديمة الخاصة بك",
|
||||||
"inputYourOldPassword": "أدخل كلمة المرور القديمة الخاصة بك",
|
"inputYourOldPassword": "أدخل كلمة المرور القديمة الخاصة بك",
|
||||||
"resetLoginPasswordtTips1": "سجل الدخول باستخدام معرف المستخدم أو معرف النمط. من الآمن أكثر تسجيل الدخول باستخدام حساب لايكي.",
|
"resetLoginPasswordtTips1": "سجّل الدخول باستخدام معرّف المستخدم أو المعرّف المميز. ويُعد تسجيل الدخول بحساب yumi أكثر أمانًا.",
|
||||||
"resetLoginPassword": "إعادة تعيين كلمة مرور تسجيل الدخول",
|
"resetLoginPassword": "إعادة تعيين كلمة مرور تسجيل الدخول",
|
||||||
"localMusic": "الموسيقى المحلية",
|
"localMusic": "الموسيقى المحلية",
|
||||||
"confirmSwitchMicThemeTips": "هل تؤكد تغيير نمط المقعد؟",
|
"confirmSwitchMicThemeTips": "هل تؤكد تغيير نمط المقعد؟",
|
||||||
"follow2": "اتبع:{1}",
|
"follow2": "المتابَعون: {1}",
|
||||||
"fans2": "المعجبون:{1}",
|
"fans2": "المعجبون:{1}",
|
||||||
"vistors2": "الزوار:{1}",
|
"vistors2": "الزوار:{1}",
|
||||||
"personal2": "شخصي:",
|
"personal2": "شخصي:",
|
||||||
@ -476,30 +476,30 @@
|
|||||||
"enterRoomName": "أدخل اسم الغرفة",
|
"enterRoomName": "أدخل اسم الغرفة",
|
||||||
"theMembershipFee": "رسوم العضوية",
|
"theMembershipFee": "رسوم العضوية",
|
||||||
"theModificationsMade": "التعديلات التي تمت هذه المرة لن يتم حفظها بعد الخروج.",
|
"theModificationsMade": "التعديلات التي تمت هذه المرة لن يتم حفظها بعد الخروج.",
|
||||||
"touristsTakeToTheMic": "السياح ياخذو الميكروفون.",
|
"touristsTakeToTheMic": "صعود الزوار إلى الميكروفون",
|
||||||
"weekly": "أسبوعي",
|
"weekly": "أسبوعي",
|
||||||
"conntinue": "استمر",
|
"conntinue": "استمر",
|
||||||
"logIn": "تسجيل الدخول",
|
"logIn": "تسجيل الدخول",
|
||||||
"sayHi": "قُل مرحبا..",
|
"sayHi": "قل مرحبًا..",
|
||||||
"password": "كلمة السر",
|
"password": "كلمة السر",
|
||||||
"enterAccount": "دخول الحساب",
|
"enterAccount": "أدخل الحساب",
|
||||||
"enterPassword": "أدخل كلمة المرور",
|
"enterPassword": "أدخل كلمة المرور",
|
||||||
"roomName": "اسم الغرفة",
|
"roomName": "اسم الغرفة",
|
||||||
"enterRoomTips": "{1} ادخل الغرفة",
|
"enterRoomTips": "{1} دخل الغرفة",
|
||||||
"startVoiceParty": "ابدأ حفلة صوتية!",
|
"startVoiceParty": "ابدأ حفلة صوتية!",
|
||||||
"freeChatSpeak": "دردش بحرية وتحدث",
|
"freeChatSpeak": "دردش بحرية وتحدث",
|
||||||
"roomMember": "عضو الغرفة",
|
"roomMember": "عضو الغرفة",
|
||||||
"popular": "مشهور",
|
"popular": "الأكثر رواجًا",
|
||||||
"coinsReceived": "العملات المستلمة",
|
"coinsReceived": "العملات المستلمة",
|
||||||
"hotRooms": "غرف حارة",
|
"hotRooms": "الغرف الرائجة",
|
||||||
"viewMore": "عرض المزيد",
|
"viewMore": "عرض المزيد",
|
||||||
"noData": "لا توجد بيانات",
|
"noData": "لا توجد بيانات",
|
||||||
"recommend": "يوصي",
|
"recommend": "موصى به",
|
||||||
"follow": "تابع",
|
"follow": "متابعة",
|
||||||
"monthly": "شهريًا",
|
"monthly": "شهريًا",
|
||||||
"message": "رسالة",
|
"message": "رسالة",
|
||||||
"bdCenter": "مركز BD",
|
"bdCenter": "مركز BD",
|
||||||
"history": "تاريخ",
|
"history": "السجل",
|
||||||
"users": "المستخدمون",
|
"users": "المستخدمون",
|
||||||
"rooms": "غرف",
|
"rooms": "غرف",
|
||||||
"days": "أيام",
|
"days": "أيام",
|
||||||
@ -507,61 +507,61 @@
|
|||||||
"unFollow": "إلغاء المتابعة",
|
"unFollow": "إلغاء المتابعة",
|
||||||
"searchInputHint": "أدخل رقم الحساب / الغرفة",
|
"searchInputHint": "أدخل رقم الحساب / الغرفة",
|
||||||
"kickRoomTips": "لقد تم طردك من الغرفة",
|
"kickRoomTips": "لقد تم طردك من الغرفة",
|
||||||
"joinRoomTips": "غرفة متضامة!",
|
"joinRoomTips": "انضم إلى الغرفة!",
|
||||||
"roomSetting": "تجهيز الغرفة",
|
"roomSetting": "إعدادات الغرفة",
|
||||||
"roomDetails": "تفاصيل الغرفة",
|
"roomDetails": "تفاصيل الغرفة",
|
||||||
"systemRoomTips": "كن مهذبا ومحترما. يُمنع منعاً باتاً أي محتوى إباحي أو مبتذل في yumi. بمجرد اكتشافه، سيتم حظر الحساب بشكل دائم. يرجى الالتزام بوعي بلوائح منصة yumi.",
|
"systemRoomTips": "كن مهذبا ومحترما. يُمنع منعاً باتاً أي محتوى إباحي أو مبتذل في yumi. بمجرد اكتشافه، سيتم حظر الحساب بشكل دائم. يرجى الالتزام بوعي بلوائح منصة yumi.",
|
||||||
"copiedToClipboard": "تم النسخ إلى الحافظة",
|
"copiedToClipboard": "تم النسخ إلى الحافظة",
|
||||||
"recharge": "إعادة الشحن",
|
"recharge": "إعادة الشحن",
|
||||||
"agentCenter": "مركز الوكالة",
|
"agentCenter": "مركز الوكالة",
|
||||||
"report": "تقرير",
|
"report": "إبلاغ",
|
||||||
"done": "تمّ",
|
"done": "تمّ",
|
||||||
"improvementTasks": "مهام التحسين",
|
"improvementTasks": "مهام التحسين",
|
||||||
"followedYou": "تابعتك",
|
"followedYou": "قام بمتابعتك",
|
||||||
"followSucc": "تمت المتابعة بنجاح",
|
"followSucc": "تمت المتابعة بنجاح",
|
||||||
"edit": "تحرير",
|
"edit": "تحرير",
|
||||||
"task": "مهمة",
|
"task": "مهمة",
|
||||||
"save": "احفظ",
|
"save": "حفظ",
|
||||||
"go": "اذهب",
|
"go": "اذهب",
|
||||||
"deleteAccount2": "حذف الحساب({1}s)",
|
"deleteAccount2": "حذف الحساب ({1}ث)",
|
||||||
"areYouSureYouWantToDeleteYourAccount": "هل أنت متأكد أنك تريد حذف حسابك؟",
|
"areYouSureYouWantToDeleteYourAccount": "هل أنت متأكد أنك تريد حذف حسابك؟",
|
||||||
"enterRoomConfirmTips": "هل أنت متأكد أنك تريد دخول الغرفة؟",
|
"enterRoomConfirmTips": "هل أنت متأكد أنك تريد دخول الغرفة؟",
|
||||||
"dailyTasks": "المهام اليومية",
|
"dailyTasks": "المهام اليومية",
|
||||||
"nickName": "لقب",
|
"nickName": "الاسم المستعار",
|
||||||
"giftSpecialEffects": "هدية المؤثرات الخاصة",
|
"giftSpecialEffects": "تأثيرات الهدايا الخاصة",
|
||||||
"country": "دولة",
|
"country": "دولة",
|
||||||
"basicFeatures": "الميزات الأساسية",
|
"basicFeatures": "الميزات الأساسية",
|
||||||
"floatingAnimationInGlobal": "الرسوم المتحركة العائمة في العالم",
|
"floatingAnimationInGlobal": "الرسوم المتحركة العائمة في العالم",
|
||||||
"joinMemberTips": "إذا كنت زائرًا في الغرفة، لا يمكنك أخذ الميكروفون.",
|
"joinMemberTips": "إذا كنت زائرًا في الغرفة، لا يمكنك أخذ الميكروفون.",
|
||||||
"countryRegion": "البلد والمنطقة",
|
"countryRegion": "البلد والمنطقة",
|
||||||
"gender": "جنس",
|
"gender": "الجنس",
|
||||||
"likedYourComment": "أعجبتني طاقتك.",
|
"likedYourComment": "أعجب بتعليقك.",
|
||||||
"deleteAccountTips2": "*إذا غيرت رأيك، يمكنك تسجيل الدخول مرة أخرى إلى حسابك الحالي خلال سبعة أيام، وسنقوم تلقائيًا باستعادة حسابك. إذا لم تتم عملية الاستعادة خلال سبعة أيام، فسيتم حذف الحساب نهائيًا",
|
"deleteAccountTips2": "*إذا غيرت رأيك، يمكنك تسجيل الدخول مرة أخرى إلى حسابك الحالي خلال سبعة أيام، وسنقوم تلقائيًا باستعادة حسابك. إذا لم تتم عملية الاستعادة خلال سبعة أيام، فسيتم حذف الحساب نهائيًا",
|
||||||
"deleteAccountTips": "لديك صلاحيات إدارية كاملة على هذا الحساب. إذا كنت تنوي حذف الحساب، يُرجى الانتباه إلى المخاطر التالية المرتبطة بهذه العملية:\n\n1. بمجرد حذف الحساب بنجاح، لن تتمكن من تسجيل الدخول إليه. حذف الحساب إجراء نهائي.\n\n2. بعد حذف الحساب بنجاح، لن تتمكن من استعادة أي بيانات. سيتم حذف جميع المعلومات (بما في ذلك الغرف، والأصدقاء)، والعملة الافتراضية، والهدايا، والعناصر الافتراضية نهائيًا ولن يكون بالإمكان استعادتها.\n\n3. فترة السماح: إذا لم تقم باستعادة الحساب، فلن تتمكن من الوصول إلى صفحة الشراء، أو صفحة السحب، أو أي صفحات أخرى في التطبيق.\n\n4. خلال فترة السماح أو بعد حذف الحساب، ستشير صفحة الملف الشخصي إلى أنه قد تم حذفه. لحماية حسابك من البحث أو الوصول إليه من قِبل الآخرين، سيتم حذف معلوماتك الشخصية من الأنظمة المتعلقة بالوظائف اليومية. عندما يتعلق حذف الحساب بالأمن القومي، أو الإجراءات المدنية أو الجنائية، أو حماية الحقوق والمصالح المشروعة لأطراف ثالثة، يحتفظ المسؤول بحقه في رفض طلب حذف حساب المستخدم.\n\nإذا كنت متأكدًا من رغبتك في حذف جميع بياناتك الشخصية من حسابك الحالي، يُرجى النقر على \"حذف الحساب\".",
|
"deleteAccountTips": "لديك صلاحيات إدارية كاملة على هذا الحساب. إذا كنت تنوي حذف الحساب، يُرجى الانتباه إلى المخاطر التالية المرتبطة بهذه العملية:\n\n1. بمجرد حذف الحساب بنجاح، لن تتمكن من تسجيل الدخول إليه. حذف الحساب إجراء نهائي.\n\n2. بعد حذف الحساب بنجاح، لن تتمكن من استعادة أي بيانات. سيتم حذف جميع المعلومات (بما في ذلك الغرف، والأصدقاء)، والعملة الافتراضية، والهدايا، والعناصر الافتراضية نهائيًا ولن يكون بالإمكان استعادتها.\n\n3. فترة السماح: إذا لم تقم باستعادة الحساب، فلن تتمكن من الوصول إلى صفحة الشراء، أو صفحة السحب، أو أي صفحات أخرى في التطبيق.\n\n4. خلال فترة السماح أو بعد حذف الحساب، ستشير صفحة الملف الشخصي إلى أنه قد تم حذفه. لحماية حسابك من البحث أو الوصول إليه من قِبل الآخرين، سيتم حذف معلوماتك الشخصية من الأنظمة المتعلقة بالوظائف اليومية. عندما يتعلق حذف الحساب بالأمن القومي، أو الإجراءات المدنية أو الجنائية، أو حماية الحقوق والمصالح المشروعة لأطراف ثالثة، يحتفظ المسؤول بحقه في رفض طلب حذف حساب المستخدم.\n\nإذا كنت متأكدًا من رغبتك في حذف جميع بياناتك الشخصية من حسابك الحالي، يُرجى النقر على \"حذف الحساب\".",
|
||||||
"accountDeletionNotice": "إشعار حذف الحساب:",
|
"accountDeletionNotice": "إشعار حذف الحساب:",
|
||||||
"entryVehicleAnimation2": "يمكن للمستخدمين الحاصلين على صلاحيات VlP4 أو أعلى استخدام هذه الوظيفة لتعطيل رسوميات المركبات المتحركة.",
|
"entryVehicleAnimation2": "يمكن للمستخدمين ذوي امتياز VIP4 أو أعلى استخدام هذه الميزة لتعطيل رسوم دخول المركبات المتحركة.",
|
||||||
"entryVehicleAnimation": "الرسوم المتحركة لدخول السيارة",
|
"entryVehicleAnimation": "الرسوم المتحركة لدخول السيارة",
|
||||||
"man": "رجل",
|
"man": "رجل",
|
||||||
"woman": "امرأة",
|
"woman": "امرأة",
|
||||||
"all": "الكل",
|
"all": "الكل",
|
||||||
"activity": "نشاط",
|
"activity": "نشاط",
|
||||||
"knapsack": "حقيبه",
|
"knapsack": "الحقيبة",
|
||||||
"areYouRureRoRecharge": "هل أنت متأكد من شحن ؟",
|
"areYouRureRoRecharge": "هل أنت متأكد أنك تريد إعادة الشحن؟",
|
||||||
"clearMessage": "مسح رسائل الشاشة",
|
"clearMessage": "مسح رسائل الشاشة",
|
||||||
"pleaseSelectaItem": "يرجى اختيار البند",
|
"pleaseSelectaItem": "يرجى اختيار عنصر",
|
||||||
"birthday": "عيد ميلاد",
|
"birthday": "عيد ميلاد",
|
||||||
"pleaseEnterNickname": "يرجى إدخال لقب.",
|
"pleaseEnterNickname": "يرجى إدخال لقب.",
|
||||||
"pleaseSelectYourCountry": "يرجى تحديد بلدك.",
|
"pleaseSelectYourCountry": "يرجى تحديد بلدك.",
|
||||||
"pleaseSelectYourGender": "يرجى تحديد جنسك.",
|
"pleaseSelectYourGender": "يرجى تحديد جنسك.",
|
||||||
"dateOfBirth": "تاريخ الميلاد",
|
"dateOfBirth": "تاريخ الميلاد",
|
||||||
"mute": "صامت",
|
"mute": "كتم",
|
||||||
"exit": "خروج",
|
"exit": "خروج",
|
||||||
"send": "أرسل",
|
"send": "أرسل",
|
||||||
"goldList": "القائمة الذهبية",
|
"goldList": "القائمة الذهبية",
|
||||||
"coins": "النقود المعدنية",
|
"coins": "عملات",
|
||||||
"lockTheRoom": "أغلق الغرفة",
|
"lockTheRoom": "أغلق الغرفة",
|
||||||
"giftGivingSuccessful": "إن تقديم الهدايا كان ناجحًا.",
|
"giftGivingSuccessful": "تم إرسال الهدية بنجاح.",
|
||||||
"hostCenter": "مركز الاستضافة",
|
"hostCenter": "مركز المضيف",
|
||||||
"allOnMicrophone": "الجميع على الميكروفون",
|
"allOnMicrophone": "الجميع على الميكروفون",
|
||||||
"usersOnMicrophone": "المستخدمون على الميكروفون",
|
"usersOnMicrophone": "المستخدمون على الميكروفون",
|
||||||
"mInimize": "احتفظ",
|
"mInimize": "احتفظ",
|
||||||
@ -572,33 +572,33 @@
|
|||||||
"admin": "المشرف",
|
"admin": "المشرف",
|
||||||
"member": "عضو",
|
"member": "عضو",
|
||||||
"guest": "ضيف",
|
"guest": "ضيف",
|
||||||
"submit": "تقديم",
|
"submit": "إرسال",
|
||||||
"enter": "دخل",
|
"enter": "دخول",
|
||||||
"unLockTheRoom": "افتح الغرفة",
|
"unLockTheRoom": "افتح الغرفة",
|
||||||
"setRoomPassword": "تعيين كلمة مرور الغرفة",
|
"setRoomPassword": "تعيين كلمة مرور الغرفة",
|
||||||
"inputRoomPassword": "إدخال كلمة مرور الغرفة",
|
"inputRoomPassword": "إدخال كلمة مرور الغرفة",
|
||||||
"operationSuccessful": "كانت العملية ناجحة.",
|
"operationSuccessful": "تمت العملية بنجاح.",
|
||||||
"adminByHomeowner": "يتم تعيين كمسؤول من قبل مالك المنزل.",
|
"adminByHomeowner": "تم تعيينه مشرفًا بواسطة مالك الغرفة.",
|
||||||
"memberByHomeowner": "يتم تعيين كأعضاء من قبل صاحب المنزل.",
|
"memberByHomeowner": "تم تعيينه عضوًا بواسطة مالك الغرفة.",
|
||||||
"touristByHomeowner": "تم تعيين كسائح من قبل صاحب المنزل.",
|
"touristByHomeowner": "تم تعيينه زائرًا بواسطة مالك الغرفة.",
|
||||||
"setUpAnIdentity": "أعد إعداد هوية.",
|
"setUpAnIdentity": "تعيين هوية",
|
||||||
"allInTheRoom": "كل شيء في الغرفة.",
|
"allInTheRoom": "الجميع داخل الغرفة",
|
||||||
"theAccountPasswordCannotBeEmpty": "لا يمكن أن يكون الحساب أو كلمة المرور خالية.",
|
"theAccountPasswordCannotBeEmpty": "لا يمكن أن يكون الحساب أو كلمة المرور خالية.",
|
||||||
"goldListort": "القائمة الذهبية",
|
"goldListort": "القائمة الذهبية",
|
||||||
"rechargeList": "قائمة إعادة الشحن",
|
"rechargeList": "قائمة إعادة الشحن",
|
||||||
"becomeHost": "قدّم لتصبح مضيفًاً",
|
"becomeHost": "التقدم لتصبح مضيفًا",
|
||||||
"alreadyAnAdministrator": "مسؤول بالفعل.ً",
|
"alreadyAnAdministrator": "هو مشرف بالفعل.",
|
||||||
"alreadyAnMember": "أنت بالفعل عضو.ً",
|
"alreadyAnMember": "هو عضو بالفعل.",
|
||||||
"touristsCannotSendMessages": "لا يستطيع السائحون إرسال الرسائل",
|
"touristsCannotSendMessages": "لا يستطيع السائحون إرسال الرسائل",
|
||||||
"touristsAreNotAllowedToGoOnTheMic": "لا يسمح للسياح بالدخول إلى الميكروفون",
|
"touristsAreNotAllowedToGoOnTheMic": "لا يُسمح للزوار بالصعود إلى الميكروفون",
|
||||||
"superFans": "المشجعين الخارقين:ً",
|
"superFans": "كبار المعجبين:",
|
||||||
"special": "خاص",
|
"special": "خاص",
|
||||||
"custom": "مخصص",
|
"custom": "مخصص",
|
||||||
"store": "دكان",
|
"store": "المتجر",
|
||||||
"viewFrame": "عرض الإطار",
|
"viewFrame": "عرض الإطار",
|
||||||
"headdress": "إطارات",
|
"headdress": "إطارات",
|
||||||
"mountains": "مركبات",
|
"mountains": "مركبات",
|
||||||
"buy": "ابتاع",
|
"buy": "شراء",
|
||||||
"visitorList": "قائمة الزوار",
|
"visitorList": "قائمة الزوار",
|
||||||
"spendCoinsToGainExperiencePoints": "اصرف العملات لكسب نقاط الخبرة",
|
"spendCoinsToGainExperiencePoints": "اصرف العملات لكسب نقاط الخبرة",
|
||||||
"howToUpgrade": "كيف يمكن الترقية؟",
|
"howToUpgrade": "كيف يمكن الترقية؟",
|
||||||
@ -609,26 +609,26 @@
|
|||||||
"obtain": "الحصول على",
|
"obtain": "الحصول على",
|
||||||
"backTheRoom": "الغرفة الخلفية",
|
"backTheRoom": "الغرفة الخلفية",
|
||||||
"toConsume": "الاستهلاك",
|
"toConsume": "الاستهلاك",
|
||||||
"profile": "بروفايل",
|
"profile": "الملف الشخصي",
|
||||||
"wallet": "محفظة",
|
"wallet": "محفظة",
|
||||||
"giftwall": "جيفت وول",
|
"giftwall": "جدار الهدايا",
|
||||||
"announcement": "إعلان",
|
"announcement": "إعلان",
|
||||||
"blockedList": "قائمة محظورة",
|
"blockedList": "قائمة الحظر",
|
||||||
"renewal": "تجديد",
|
"renewal": "تجديد",
|
||||||
"country2": "بلد:",
|
"country2": "بلد:",
|
||||||
"sendTo": "أرسل إلى",
|
"sendTo": "أرسل إلى",
|
||||||
"credits": "الاعتمادات: {1}",
|
"credits": "الاعتمادات: {1}",
|
||||||
"successfullyUnloaded": "تم تفريغها بنجاح",
|
"successfullyUnloaded": "تمت الإزالة بنجاح",
|
||||||
"unUse": "استخدام واحد",
|
"unUse": "إزالة الاستخدام",
|
||||||
"successfulWear": "ملابس ناجحة",
|
"successfulWear": "تم الارتداء بنجاح",
|
||||||
"confirmUnUseTips": "هل تؤكد على إزالته؟",
|
"confirmUnUseTips": "هل تؤكد إزالة استخدامه؟",
|
||||||
"inUse": "قيد الاستخدام",
|
"inUse": "قيد الاستخدام",
|
||||||
"confirmUseTips": "هل تريد التأكيد على استخدامه؟",
|
"confirmUseTips": "هل تريد التأكيد على استخدامه؟",
|
||||||
"pleaseUploadUserAvatar": "يرجى رفع صورة شخصية.",
|
"pleaseUploadUserAvatar": "يرجى رفع صورة شخصية.",
|
||||||
"myItems": "أشيائي",
|
"myItems": "مقتنياتي",
|
||||||
"confirmBuyTips": "هل أنت متأكد من أنك تريد الشراء؟",
|
"confirmBuyTips": "هل أنت متأكد من أنك تريد الشراء؟",
|
||||||
"purchaseIsSuccessful": "الشراء ناجح",
|
"purchaseIsSuccessful": "الشراء ناجح",
|
||||||
"purchase": "ابتاع",
|
"purchase": "شراء",
|
||||||
"invitesYouToTheMicrophone": "{1} يدعوك إلى الميكروفون",
|
"invitesYouToTheMicrophone": "{1} يدعوك إلى الميكروفون",
|
||||||
"english": "الإنجليزية",
|
"english": "الإنجليزية",
|
||||||
"chinese": "الصينية",
|
"chinese": "الصينية",
|
||||||
@ -636,6 +636,9 @@
|
|||||||
"darkMode": "الوضع الداكن",
|
"darkMode": "الوضع الداكن",
|
||||||
"lightMode": "الوضع الفاتح",
|
"lightMode": "الوضع الفاتح",
|
||||||
"systemDefault": "النظام الافتراضي",
|
"systemDefault": "النظام الافتراضي",
|
||||||
"pleaseGetOnTheMicFirst": "من فضلك استخدم الميكروفون أولاً.",
|
"pleaseGetOnTheMicFirst": "يرجى الصعود إلى الميكروفون أولاً.",
|
||||||
|
"welcomeMessage": "مرحبًا بك في تطبيقنا، {name}!",
|
||||||
|
"operationFail": "فشلت العملية.",
|
||||||
|
"doYouWantToKeepTheDraft": "هل تريد الاحتفاظ بالمسودة؟",
|
||||||
"duration2": "المدة:{1}"
|
"duration2": "المدة:{1}"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@
|
|||||||
"privaceyPolicy": "Gizlilik Politikası",
|
"privaceyPolicy": "Gizlilik Politikası",
|
||||||
"tips": "İpuçları",
|
"tips": "İpuçları",
|
||||||
"searchNoDataTips": "Aramak istediğiniz oda veya kullanıcı kimliğini girin.",
|
"searchNoDataTips": "Aramak istediğiniz oda veya kullanıcı kimliğini girin.",
|
||||||
"mine": "Benimki",
|
"mine": "Benim",
|
||||||
"party": "Parti",
|
"party": "Parti",
|
||||||
"myRoom": "Odam",
|
"myRoom": "Odam",
|
||||||
"other": "Diğer",
|
"other": "Diğer",
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"blockedList2": "Engellenen Liste",
|
"blockedList2": "Engellenen Liste",
|
||||||
"roomTheme2": "Oda Teması",
|
"roomTheme2": "Oda Teması",
|
||||||
"roomPassword": "Oda Şifresi",
|
"roomPassword": "Oda Şifresi",
|
||||||
"noHistoricalRecordsAvailable": "Mevcut tarihi kayıt yok.",
|
"noHistoricalRecordsAvailable": "Geçmiş kayıt bulunamadı.",
|
||||||
"inviteNewUsersToEarnCoins": "Yeni kullanıcıları davet ederek coin kazanın",
|
"inviteNewUsersToEarnCoins": "Yeni kullanıcıları davet ederek coin kazanın",
|
||||||
"crateMyRoom": "KENDİ ODANIZI OLUŞTURUN.",
|
"crateMyRoom": "KENDİ ODANIZI OLUŞTURUN.",
|
||||||
"event": "Etkinlik",
|
"event": "Etkinlik",
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"trend": "Trend",
|
"trend": "Trend",
|
||||||
"like": "Beğen",
|
"like": "Beğen",
|
||||||
"more": "Daha Fazla",
|
"more": "Daha Fazla",
|
||||||
"discard": "Vur",
|
"discard": "Vazgeç",
|
||||||
"catchFirstComment": "İlk Yorumu Yakala",
|
"catchFirstComment": "İlk Yorumu Yakala",
|
||||||
"reply": "Cevapla",
|
"reply": "Cevapla",
|
||||||
"posting": "Gönderiliyor",
|
"posting": "Gönderiliyor",
|
||||||
@ -66,9 +66,9 @@
|
|||||||
"systemAnnouncementTips1": "Sahtekarlığa karşı dikkat:",
|
"systemAnnouncementTips1": "Sahtekarlığa karşı dikkat:",
|
||||||
"systemAnnouncementTips": "Bilgileri yalnızca resmi kanallar aracılığıyla doğrulayın. Hiçbir zaman üçüncü taraf yazılımı indirme, kişisel verileri paylaşma veya dış istekler üzerine para transferi yapmayın. Resmi personel kimlik numaraları yalnızca 10000, 10003 ve 10086'dır. Herhangi bir şüpheniz olursa, işlemi durdurun ve üzerinden bildirin",
|
"systemAnnouncementTips": "Bilgileri yalnızca resmi kanallar aracılığıyla doğrulayın. Hiçbir zaman üçüncü taraf yazılımı indirme, kişisel verileri paylaşma veya dış istekler üzerine para transferi yapmayın. Resmi personel kimlik numaraları yalnızca 10000, 10003 ve 10086'dır. Herhangi bir şüpheniz olursa, işlemi durdurun ve üzerinden bildirin",
|
||||||
"systemAnnouncement": "Sistem Duyurusu",
|
"systemAnnouncement": "Sistem Duyurusu",
|
||||||
"doNotClickUnfamiliarTips": "Tanımadığınız bağlantılara tıklayın, çünkü bunlar kişisel bilgilerinizi ifşa edebilir. Kimlik numaranızı veya banka kartı detaylarınızı asla kimseyle paylaşmayın.",
|
"doNotClickUnfamiliarTips": "Tanımadığınız bağlantılara tıklamayın; bunlar kişisel bilgilerinizi açığa çıkarabilir. Kimlik numaranızı veya banka kartı bilgilerinizi asla kimseyle paylaşmayın.",
|
||||||
"atTag": "@Etiket",
|
"atTag": "@Etiket",
|
||||||
"sayHi2": "Merhaba De",
|
"sayHi2": "Merhaba",
|
||||||
"roomBottomGreeting": "Merhaba...",
|
"roomBottomGreeting": "Merhaba...",
|
||||||
"canSendMsgTips": "Özel mesaj göndermek için her iki tarafın da birbirini takip etmesi gerekir.",
|
"canSendMsgTips": "Özel mesaj göndermek için her iki tarafın da birbirini takip etmesi gerekir.",
|
||||||
"msgSendRedEnvelopeTips": "*Kırmızı zarflar üzerinde %10 hizmet ücreti kesilecektir ve alıcılar yalnızca kırmızı zarfın değerinin %90'ını alacaktır. Göndericinin servet seviyesi 10. Seviyeden yüksek olmalıdır.",
|
"msgSendRedEnvelopeTips": "*Kırmızı zarflar üzerinde %10 hizmet ücreti kesilecektir ve alıcılar yalnızca kırmızı zarfın değerinin %90'ını alacaktır. Göndericinin servet seviyesi 10. Seviyeden yüksek olmalıdır.",
|
||||||
@ -83,7 +83,7 @@
|
|||||||
"memberList": "Üye Listesi",
|
"memberList": "Üye Listesi",
|
||||||
"treasureChest": "Hazine Sandığı",
|
"treasureChest": "Hazine Sandığı",
|
||||||
"applicationRecord": "Başvuru Kaydı",
|
"applicationRecord": "Başvuru Kaydı",
|
||||||
"appUpdateTip": "Uygulamanın yeni bir sürümü var ({1}), lütfen indirin mi?",
|
"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",
|
||||||
@ -104,7 +104,7 @@
|
|||||||
"termsOfServicePrivacyPolicyTips": "Devam ederek Hizmet Şartları'nı ve Gizlilik Politikası'nı kabul ediyorsunuz",
|
"termsOfServicePrivacyPolicyTips": "Devam ederek Hizmet Şartları'nı ve Gizlilik Politikası'nı kabul ediyorsunuz",
|
||||||
"and": " ve ",
|
"and": " ve ",
|
||||||
"pleaseSelectTheTypeContent": "Lütfen ihlalci içeriğin türünü seçin.",
|
"pleaseSelectTheTypeContent": "Lütfen ihlalci içeriğin türünü seçin.",
|
||||||
"illegalInformation": "Yasaksız Bilgi",
|
"illegalInformation": "Yasa Dışı Bilgi",
|
||||||
"inappropriateContent": "Uygunsuz İçerik",
|
"inappropriateContent": "Uygunsuz İçerik",
|
||||||
"personalAttack": "Kişisel Saldırı",
|
"personalAttack": "Kişisel Saldırı",
|
||||||
"confirm": "Onayla",
|
"confirm": "Onayla",
|
||||||
@ -122,14 +122,14 @@
|
|||||||
"lastWeekProgress": "Geçen Haftanın İlerlemesi",
|
"lastWeekProgress": "Geçen Haftanın İlerlemesi",
|
||||||
"redEnvelopeTips2": "*Kırmızı zarf zaman sınırı içinde talep edilmezse, kalan jettonlar gönderen kullanıcıya iade edilecektir.",
|
"redEnvelopeTips2": "*Kırmızı zarf zaman sınırı içinde talep edilmezse, kalan jettonlar gönderen kullanıcıya iade edilecektir.",
|
||||||
"goToRecharge": "Yüklemeye Git",
|
"goToRecharge": "Yüklemeye Git",
|
||||||
"deleteAccount2": " Hesabı Sil({1}s)",
|
"deleteAccount2": " Hesabı Sil ({1} sn)",
|
||||||
"areYouSureYouWantToDeleteYourAccount": " Hesabınızı silmek istediğinizden emin misiniz?",
|
"areYouSureYouWantToDeleteYourAccount": " Hesabınızı silmek istediğinizden emin misiniz?",
|
||||||
"insufhcientGoldsGoToRecharge": "Altın yetersiz, hemen yükle!",
|
"insufhcientGoldsGoToRecharge": "Altın yetersiz, hemen yükle!",
|
||||||
"coins2": "{1}Jetton",
|
"coins2": "{1} Jetton",
|
||||||
"remainingNumberTips": "Kalan Kullanılabilir Sayı:({1}/{2})",
|
"remainingNumberTips": "Kalan Kullanılabilir Sayı:({1}/{2})",
|
||||||
"collectionTimeTips": "Toplama Zamanı:{1}({2}/{3})",
|
"collectionTimeTips": "Toplama Zamanı:{1}({2}/{3})",
|
||||||
"sendARedEnvelope": "Kırmızı Zarf Gönder",
|
"sendARedEnvelope": "Kırmızı Zarf Gönder",
|
||||||
"sendRedPackConfirmTips": "Kırmızı paket göndermek istediğinizden emin misiniz?",
|
"sendRedPackConfirmTips": "Kırmızı zarfı göndermek istediğinizden emin misiniz?",
|
||||||
"redEnvelopeSendingRecords": "Kırmızı zarf gönderim kayıtları:",
|
"redEnvelopeSendingRecords": "Kırmızı zarf gönderim kayıtları:",
|
||||||
"redEnvelope": "Kırmızı Zarf",
|
"redEnvelope": "Kırmızı Zarf",
|
||||||
"redEnvelopeRecTips2": "Kırmızı zarfların hepsi talep edildi.",
|
"redEnvelopeRecTips2": "Kırmızı zarfların hepsi talep edildi.",
|
||||||
@ -139,7 +139,7 @@
|
|||||||
"redEnvelopeTips1": "Jettonlar:",
|
"redEnvelopeTips1": "Jettonlar:",
|
||||||
"roomTools": "Oda Araçları:",
|
"roomTools": "Oda Araçları:",
|
||||||
"entertainment": "Eğlence:",
|
"entertainment": "Eğlence:",
|
||||||
"reportSucc": "Bildirim başarılı",
|
"reportSucc": "Bildirim başarıyla gönderildi",
|
||||||
"pornography": "Pornografi",
|
"pornography": "Pornografi",
|
||||||
"reportInputTips": "Sorunu anlayabilmemiz ve çözebilmemiz için lütfen sorunu mümkün olduğunca detaylı anlatın.",
|
"reportInputTips": "Sorunu anlayabilmemiz ve çözebilmemiz için lütfen sorunu mümkün olduğunca detaylı anlatın.",
|
||||||
"cancel": "İptal",
|
"cancel": "İptal",
|
||||||
@ -173,7 +173,7 @@
|
|||||||
"youHaventFollowed": "Hiç oda takip etmediniz",
|
"youHaventFollowed": "Hiç oda takip etmediniz",
|
||||||
"deleteFromMyDevice": "Cihazımdan Sil",
|
"deleteFromMyDevice": "Cihazımdan Sil",
|
||||||
"deleteOnAllDevices": "Tüm Cihazlarda Sil",
|
"deleteOnAllDevices": "Tüm Cihazlarda Sil",
|
||||||
"messageHasBeenRecalled": "Bu mesaj geri çağırıldı",
|
"messageHasBeenRecalled": "Bu mesaj geri çekildi",
|
||||||
"recallThisMessage": "Bu mesajı geri çağırmak ister misiniz?",
|
"recallThisMessage": "Bu mesajı geri çağırmak ister misiniz?",
|
||||||
"language": "Dil",
|
"language": "Dil",
|
||||||
"feedback": "Geri Bildirim",
|
"feedback": "Geri Bildirim",
|
||||||
@ -188,7 +188,7 @@
|
|||||||
"logout": "Çıkış Yap",
|
"logout": "Çıkış Yap",
|
||||||
"luck": "Şans",
|
"luck": "Şans",
|
||||||
"level": "Seviye",
|
"level": "Seviye",
|
||||||
"themeGoToUploadTips": "1.Yükleme başarılı olduktan sonra 24 saat içinde inceleme yapılacaktır.\n2.Inceleme başarısız olursa tüm jettonlar iade edilecektir.",
|
"themeGoToUploadTips": "1. Yükleme başarılı olduktan sonra 24 saat içinde inceleme yapılacaktır.\n2. İnceleme başarısız olursa tüm jettonlar iade edilecektir.",
|
||||||
"home": "Anasayfa",
|
"home": "Anasayfa",
|
||||||
"explore": "Keşfet",
|
"explore": "Keşfet",
|
||||||
"me": "Ben",
|
"me": "Ben",
|
||||||
@ -210,14 +210,14 @@
|
|||||||
"areYouSureYouWantToSpend2": "bu kullanıcıya CP daveti göndermek için?",
|
"areYouSureYouWantToSpend2": "bu kullanıcıya CP daveti göndermek için?",
|
||||||
"underReview": "İnceleniyor",
|
"underReview": "İnceleniyor",
|
||||||
"doYouWantToDeleteIt": "Silmek ister misiniz?",
|
"doYouWantToDeleteIt": "Silmek ister misiniz?",
|
||||||
"chooseFromAblum": "Albümdan Seç",
|
"chooseFromAblum": "Albümden Seç",
|
||||||
"spaceBackground": "Mekan Arka Planı",
|
"spaceBackground": "Mekan Arka Planı",
|
||||||
"editProfile": "Profili Düzenle",
|
"editProfile": "Profili Düzenle",
|
||||||
"sendTheCpRequest": "CP İsteğini Gönder",
|
"sendTheCpRequest": "CP İsteğini Gönder",
|
||||||
"addCp": "CP Ekle",
|
"addCp": "CP Ekle",
|
||||||
"partWays": "Yollarını Ayırmak",
|
"partWays": "Yolları Ayır",
|
||||||
"reconcile": "Uzlaşmak",
|
"reconcile": "Uzlaşmak",
|
||||||
"separated": "Ayırılmış",
|
"separated": "Ayrıldı",
|
||||||
"areYouSureYouWantToSpend5": "{1} size hislerini ifade etti; kabul ederseniz çift olacaksınız.",
|
"areYouSureYouWantToSpend5": "{1} size hislerini ifade etti; kabul ederseniz çift olacaksınız.",
|
||||||
"areYouSureYouWantToSpend6": "{1} sizinle tekrar bir araya gelmek istiyor. Uzlaşmaya karar verirseniz, tüm önceki verileriniz geri yüklenecektir.",
|
"areYouSureYouWantToSpend6": "{1} sizinle tekrar bir araya gelmek istiyor. Uzlaşmaya karar verirseniz, tüm önceki verileriniz geri yüklenecektir.",
|
||||||
"reconcileInvitationTips": "*Diğer taraf CP davetini reddederse, jettonlarınız cüzdanınıza iade edilecektir.",
|
"reconcileInvitationTips": "*Diğer taraf CP davetini reddederse, jettonlarınız cüzdanınıza iade edilecektir.",
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"props": "Özellikler",
|
"props": "Özellikler",
|
||||||
"win": "Kazanan",
|
"win": "Kazanan",
|
||||||
"dice": "Zar",
|
"dice": "Zar",
|
||||||
"rps": "Kağıt-Kaçak-Makas",
|
"rps": "Taş-Kağıt-Makas",
|
||||||
"operationFail": "İşlem başarısız oldu.",
|
"operationFail": "İşlem başarısız oldu.",
|
||||||
"likedYourComment": "Yorumunuzu beğendi.",
|
"likedYourComment": "Yorumunuzu beğendi.",
|
||||||
"doYouWantToKeepTheDraft": "Taslağı saklamak ister misiniz?",
|
"doYouWantToKeepTheDraft": "Taslağı saklamak ister misiniz?",
|
||||||
@ -262,19 +262,19 @@
|
|||||||
"roomAnnouncement": "Oda Duyurusu",
|
"roomAnnouncement": "Oda Duyurusu",
|
||||||
"help": "Yardım",
|
"help": "Yardım",
|
||||||
"rejected": "Reddedildi",
|
"rejected": "Reddedildi",
|
||||||
"boxContributeTips": "Bugün zaten yatırım yapıldı, lütfen tekrar yatırım yapmayın",
|
"boxContributeTips": "Bugün zaten yatırım yapıldı, lütfen tekrar yatırım yapmayın.",
|
||||||
"bd": "BD",
|
"bd": "BD",
|
||||||
"coupon": "Kupon",
|
"coupon": "Kupon",
|
||||||
"search": "Ara",
|
"search": "Ara",
|
||||||
"get": "Al",
|
"get": "Al",
|
||||||
"inRocket": "Rokette",
|
"inRocket": "Rokette",
|
||||||
"roomRocketHelpTips": "1. Odada hediye göndermek roket enerjisini artırır. *1 altın jetton hediyesi = 1 roket enerji puanı; şanslı hediyeler roket enerjisini hediyenin altın jetton değerinin %4'ü kadar artırır.\n2. Roket enerjisi tamamen dolduğunda, oda roketi fırlatabilir. Fırlatmadan sonra ödüller otomatik olarak dağıtılacaktır.\n3. Farklı roket seviyeleri farklı ödüller sunar.\n4. Roket fırlatıldığında, odadaki tüm kullanıcılar roket ödülünü talep edebilir.5. Roket enerjisi her gün 00:00'da sıfırlanır.",
|
"roomRocketHelpTips": "1. Odada hediye göndermek roket enerjisini artırır. *1 altın jetton hediyesi = 1 roket enerji puanı; şanslı hediyeler roket enerjisini hediyenin altın jetton değerinin %4'ü kadar artırır.\n2. Roket enerjisi tamamen dolduğunda, oda roketi fırlatabilir. Fırlatmadan sonra ödüller otomatik olarak dağıtılacaktır.\n3. Farklı roket seviyeleri farklı ödüller sunar.\n4. Roket fırlatıldığında, odadaki tüm kullanıcılar roket ödülünü talep edebilir.\n5. Roket enerjisi her gün 00:00'da sıfırlanır.",
|
||||||
"couponRecord": "Kupon Kullanım Kaydı",
|
"couponRecord": "Kupon Kullanım Kaydı",
|
||||||
"inRoom": "Odada",
|
"inRoom": "Odada",
|
||||||
"searchCouponHint": "Kupon Ara",
|
"searchCouponHint": "Kupon Ara",
|
||||||
"giftCounter": "Hediye Sayacı",
|
"giftCounter": "Hediye Sayacı",
|
||||||
"bDLeaderInviteYouToBecomeBDLeader": "Sizi BD Lideri yapmaya davet ediyoruz",
|
"bDLeaderInviteYouToBecomeBDLeader": "Sizi BD Lideri yapmaya davet ediyoruz",
|
||||||
"wins": "kazanmalar",
|
"wins": "kazandı",
|
||||||
"inviteYouToBecomeHost": "Sizi sunucu yapmaya davet ediyoruz.",
|
"inviteYouToBecomeHost": "Sizi sunucu yapmaya davet ediyoruz.",
|
||||||
"friends": "Arkadaşlar",
|
"friends": "Arkadaşlar",
|
||||||
"deleteConversationTips": "Bu kullanıcıyla olan sohbet geçmişini silmek istediğinizden emin misiniz?",
|
"deleteConversationTips": "Bu kullanıcıyla olan sohbet geçmişini silmek istediğinizden emin misiniz?",
|
||||||
@ -285,7 +285,7 @@
|
|||||||
"checkInSuccessful": "Giriş başarılı",
|
"checkInSuccessful": "Giriş başarılı",
|
||||||
"sginTips": "Her gün ilk defa giriş yaptığınızda ödül alacaksınız. Girişi keserseniz, tekrar giriş yaptığınızda ödül ilk günden itibaren hesaplanacaktır.",
|
"sginTips": "Her gün ilk defa giriş yaptığınızda ödül alacaksınız. Girişi keserseniz, tekrar giriş yaptığınızda ödül ilk günden itibaren hesaplanacaktır.",
|
||||||
"popular": "Popüler",
|
"popular": "Popüler",
|
||||||
"recommend": "Öner",
|
"recommend": "Önerilen",
|
||||||
"follow": "Takip Et",
|
"follow": "Takip Et",
|
||||||
"history": "Tarihçe",
|
"history": "Tarihçe",
|
||||||
"hotRooms": "Popüler Odalar",
|
"hotRooms": "Popüler Odalar",
|
||||||
@ -347,7 +347,7 @@
|
|||||||
"localMusic": "Yerel Müzik",
|
"localMusic": "Yerel Müzik",
|
||||||
"setLoginPassword": "Giriş Şifresi Ayarlayın",
|
"setLoginPassword": "Giriş Şifresi Ayarlayın",
|
||||||
"confirmSwitchMicThemeTips": "Koltuk stili değiştirmeyi onaylıyor musunuz?",
|
"confirmSwitchMicThemeTips": "Koltuk stili değiştirmeyi onaylıyor musunuz?",
|
||||||
"micTheme": "Mikro Tema",
|
"micTheme": "Mikrofon Teması",
|
||||||
"classicMic": "Klasik {1} Mikro",
|
"classicMic": "Klasik {1} Mikro",
|
||||||
"yesterday": "Dün {1}",
|
"yesterday": "Dün {1}",
|
||||||
"monday": "Pazartesi {1}",
|
"monday": "Pazartesi {1}",
|
||||||
@ -375,7 +375,7 @@
|
|||||||
"warning": "Uyarı",
|
"warning": "Uyarı",
|
||||||
"screenshotTips": "Ekran Görüntüsü (En Fazla 3)",
|
"screenshotTips": "Ekran Görüntüsü (En Fazla 3)",
|
||||||
"roomNotice": "Oda Bildirimi",
|
"roomNotice": "Oda Bildirimi",
|
||||||
"roomTheme": "Oda Tema",
|
"roomTheme": "Oda Teması",
|
||||||
"description": "Açıklama:",
|
"description": "Açıklama:",
|
||||||
"inputDesHint": "Sorunu anlayabilmemiz ve çözebilmemiz için lütfen sorunu mümkün olduğunca detaylı anlatın.",
|
"inputDesHint": "Sorunu anlayabilmemiz ve çözebilmemiz için lütfen sorunu mümkün olduğunca detaylı anlatın.",
|
||||||
"roomProfilePicture": "Oda Profil Fotoğrafı",
|
"roomProfilePicture": "Oda Profil Fotoğrafı",
|
||||||
@ -408,7 +408,7 @@
|
|||||||
"joinRoomTips": "odaya katıldı !",
|
"joinRoomTips": "odaya katıldı !",
|
||||||
"roomSetting": "Oda Ayarları",
|
"roomSetting": "Oda Ayarları",
|
||||||
"roomDetails": "Oda Detayları",
|
"roomDetails": "Oda Detayları",
|
||||||
"systemRoomTips": "Neysevi ve saygılı olun. yumi'de herhangi bir pornografik veya uygunsuz içerik kesinlikle yasaktır. Keşfedilirse, hesap kalıcı olarak engellenecektir. Lütfen yumi platformunun düzenlemelerini bilinçli olarak takip edin.",
|
"systemRoomTips": "Nezaketli ve saygılı olun. yumi'de pornografik veya uygunsuz içerik kesinlikle yasaktır. Tespit edilmesi halinde hesap kalıcı olarak engellenecektir. Lütfen yumi platform kurallarına bilinçli şekilde uyun.",
|
||||||
"copiedToClipboard": "Panoya kopyalandı",
|
"copiedToClipboard": "Panoya kopyalandı",
|
||||||
"recharge": "Yükle",
|
"recharge": "Yükle",
|
||||||
"receivedFromALuckyGift": "Şanslı/sihirli bir hediyeden alındı.",
|
"receivedFromALuckyGift": "Şanslı/sihirli bir hediyeden alındı.",
|
||||||
@ -423,7 +423,7 @@
|
|||||||
"task": "Görev",
|
"task": "Görev",
|
||||||
"importantReminder": "Önemli Hatırlatma",
|
"importantReminder": "Önemli Hatırlatma",
|
||||||
"entryVehicleAnimation": "Giriş Aracı Animasyonu",
|
"entryVehicleAnimation": "Giriş Aracı Animasyonu",
|
||||||
"floatingAnimationInGlobal": "Küreselde Süzme Animasyon",
|
"floatingAnimationInGlobal": "Genel Ekranda Yüzen Animasyon",
|
||||||
"entryVehicleAnimation2": "VIP4 veya daha yüksek haklara sahip kullanıcılar araç animasyonlarını devre dışı bırakmak için fonksiyonu kullanabilir.",
|
"entryVehicleAnimation2": "VIP4 veya daha yüksek haklara sahip kullanıcılar araç animasyonlarını devre dışı bırakmak için fonksiyonu kullanabilir.",
|
||||||
"dailyTasks": "Günlük Görevler",
|
"dailyTasks": "Günlük Görevler",
|
||||||
"enterRoomConfirmTips": "Odaya girmek istediğinizden emin misiniz?",
|
"enterRoomConfirmTips": "Odaya girmek istediğinizden emin misiniz?",
|
||||||
@ -431,7 +431,7 @@
|
|||||||
"goldListort": "Altın Listesi",
|
"goldListort": "Altın Listesi",
|
||||||
"rechargeList": "Yükleme Listesi",
|
"rechargeList": "Yükleme Listesi",
|
||||||
"edit": "Düzenle",
|
"edit": "Düzenle",
|
||||||
"swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*İpucu: Süzme ekran alanına sola kaydırarak hızlıca kapatın.",
|
"swipeLeftOnTheFloatingScreenAreaToQuicklyCloseIt": "*İpucu: Hızlıca kapatmak için yüzen ekran alanında sola kaydırın.",
|
||||||
"enterThisVoiceChatRoom": "Bu sesli sohbet odasına girmek ister misiniz?",
|
"enterThisVoiceChatRoom": "Bu sesli sohbet odasına girmek ister misiniz?",
|
||||||
"go": "Git",
|
"go": "Git",
|
||||||
"done": "Bitti",
|
"done": "Bitti",
|
||||||
@ -453,14 +453,14 @@
|
|||||||
"userLevelXPBoost": "Kullanıcı Seviyesi XP Artışı({1} XP)",
|
"userLevelXPBoost": "Kullanıcı Seviyesi XP Artışı({1} XP)",
|
||||||
"google": "Google",
|
"google": "Google",
|
||||||
"mysteriousInvisibility": "Gizemli görünmezlik",
|
"mysteriousInvisibility": "Gizemli görünmezlik",
|
||||||
"antiBlock": "Tıkanmayı Önleyici",
|
"antiBlock": "Engellenme Koruması",
|
||||||
"privateChat": "Özel Sohbet",
|
"privateChat": "Özel Sohbet",
|
||||||
"everyone": "Herkes",
|
"everyone": "Herkes",
|
||||||
"goToUpgrade": "Yükseltmeye git",
|
"goToUpgrade": "Yükseltmeye git",
|
||||||
"exclusiveEmojiWillBeReleasedAfterBecoming": "Özel emoji olunduktan sonra yayınlanacak",
|
"exclusiveEmojiWillBeReleasedAfterBecoming": "Özel emoji olunduktan sonra yayınlanacak",
|
||||||
"preventBeingBlocked": "Engellenmekten Kaçının",
|
"preventBeingBlocked": "Engellenmekten Kaçının",
|
||||||
"enableRankIncognitoMode": "Rütbe Gizli Modunu Etkinleştir",
|
"enableRankIncognitoMode": "Rütbe Gizli Modunu Etkinleştir",
|
||||||
"avoidBeingKicked": "Tekme Yemekten Kaçının",
|
"avoidBeingKicked": "Odadan Atılmayı Önleme",
|
||||||
"privileges": "{1} Ayrıcalıklar",
|
"privileges": "{1} Ayrıcalıklar",
|
||||||
"andAboveUsers": "{1} ve üzeri kullanıcılar",
|
"andAboveUsers": "{1} ve üzeri kullanıcılar",
|
||||||
"basicPermissions": "Temel izinler",
|
"basicPermissions": "Temel izinler",
|
||||||
@ -481,7 +481,7 @@
|
|||||||
"exit": "Çıkış",
|
"exit": "Çıkış",
|
||||||
"pleaseSelectaItem": "Lütfen bir öğe seçin",
|
"pleaseSelectaItem": "Lütfen bir öğe seçin",
|
||||||
"areYouRureRoRecharge": "Yüklemek istediğinizden emin misiniz?",
|
"areYouRureRoRecharge": "Yüklemek istediğinizden emin misiniz?",
|
||||||
"mInimize": "Tutmak",
|
"mInimize": "Küçült",
|
||||||
"shop": "Mağaza",
|
"shop": "Mağaza",
|
||||||
"expirationTime": "Son kullanma süresi",
|
"expirationTime": "Son kullanma süresi",
|
||||||
"roomOwner": "Oda Sahibi",
|
"roomOwner": "Oda Sahibi",
|
||||||
@ -495,13 +495,13 @@
|
|||||||
"takeTheMic": "Mikroyu Al",
|
"takeTheMic": "Mikroyu Al",
|
||||||
"openTheMic": "Mikroyu Aç",
|
"openTheMic": "Mikroyu Aç",
|
||||||
"muteTheMic": "Mikronun Sesini Kapat",
|
"muteTheMic": "Mikronun Sesini Kapat",
|
||||||
"unlockTheMic": "Mikroyu Kilidini Aç",
|
"unlockTheMic": "Mikrofon Kilidini Aç",
|
||||||
"leavelTheMic": "Mikroyu Bırak",
|
"leavelTheMic": "Mikroyu Bırak",
|
||||||
"lockTheMic": "Mikroyu Kilitle",
|
"lockTheMic": "Mikroyu Kilitle",
|
||||||
"removeTheMic": "Mikroyu Kaldır",
|
"removeTheMic": "Mikroyu Kaldır",
|
||||||
"inviteToTheMicrophone": "Mikroya Davet Et",
|
"inviteToTheMicrophone": "Mikroya Davet Et",
|
||||||
"openUserProfleCard": "Kullanıcı Profili Kartını Aç",
|
"openUserProfleCard": "Kullanıcı Profili Kartını Aç",
|
||||||
"obtain": "elde etmek",
|
"obtain": "Al",
|
||||||
"win2": "{1} Kazan",
|
"win2": "{1} Kazan",
|
||||||
"backTheRoom": "Odaya Dön",
|
"backTheRoom": "Odaya Dön",
|
||||||
"toConsume": "Tüketmek İçin",
|
"toConsume": "Tüketmek İçin",
|
||||||
@ -519,10 +519,10 @@
|
|||||||
"submit": "Gönder",
|
"submit": "Gönder",
|
||||||
"membershipFee": "Üyelik Ücreti",
|
"membershipFee": "Üyelik Ücreti",
|
||||||
"membershipFeeTips1": "Lütfen odanız için üyelik ücretini ayarlayın. Kullanıcılar ücreti ödeyerek odanıza katılabilir.",
|
"membershipFeeTips1": "Lütfen odanız için üyelik ücretini ayarlayın. Kullanıcılar ücreti ödeyerek odanıza katılabilir.",
|
||||||
"membershipFeeTips2": "Kullanıcı'nın oda üyesi olması için gerekli altınlar.Oda sahibi altınların %50'sini alacaktır.",
|
"membershipFeeTips2": "Kullanıcının oda üyesi olması için gereken altın miktarıdır. Oda sahibi altınların %50'sini alacaktır.",
|
||||||
"freePrice": "Ücret:0-10000",
|
"freePrice": "Ücret:0-10000",
|
||||||
"touristsSendText": "Turist metin gönderir",
|
"touristsSendText": "Ziyaretçi metin gönderebilir",
|
||||||
"touristsTakeToTheMic": "Turist mikro alır",
|
"touristsTakeToTheMic": "Ziyaretçi mikrofona geçebilir",
|
||||||
"theMembershipFee": "Üyelik Ücreti",
|
"theMembershipFee": "Üyelik Ücreti",
|
||||||
"theModificationsMade": "Bu sefer yapılan değişiklikler çıkıştan sonra kaydedilmeyecektir",
|
"theModificationsMade": "Bu sefer yapılan değişiklikler çıkıştan sonra kaydedilmeyecektir",
|
||||||
"viewFrame": "Çerçeveyi Gör",
|
"viewFrame": "Çerçeveyi Gör",
|
||||||
@ -556,7 +556,7 @@
|
|||||||
"saySomething": "Bir şey söyle...",
|
"saySomething": "Bir şey söyle...",
|
||||||
"sayHi": "Merhaba..",
|
"sayHi": "Merhaba..",
|
||||||
"pleaseChatFfriendly": "Lütfen arkadaşça sohbet edin",
|
"pleaseChatFfriendly": "Lütfen arkadaşça sohbet edin",
|
||||||
"unLockTheRoom": "Odayı Kilidini Aç",
|
"unLockTheRoom": "Odanın Kilidini Aç",
|
||||||
"operationSuccessful": "İşlem başarılı oldu.",
|
"operationSuccessful": "İşlem başarılı oldu.",
|
||||||
"adminByHomeowner": "ev sahibi tarafından yönetici olarak atandı.",
|
"adminByHomeowner": "ev sahibi tarafından yönetici olarak atandı.",
|
||||||
"memberByHomeowner": "ev sahibi tarafından üye olarak atandı.",
|
"memberByHomeowner": "ev sahibi tarafından üye olarak atandı.",
|
||||||
@ -568,7 +568,7 @@
|
|||||||
"knapsack": "Sırt Çantası",
|
"knapsack": "Sırt Çantası",
|
||||||
"bdLeader": "BD Lideri",
|
"bdLeader": "BD Lideri",
|
||||||
"picture": "Resim",
|
"picture": "Resim",
|
||||||
"claim": "İddia",
|
"claim": "Talep Et",
|
||||||
"taskNameRoomNewMember": "Yeni Oda Üyeleri",
|
"taskNameRoomNewMember": "Yeni Oda Üyeleri",
|
||||||
"taskNameRoomOwnerSendRedPacket": "Oda Sahibi bir kırmızı paket gönderiyor",
|
"taskNameRoomOwnerSendRedPacket": "Oda Sahibi bir kırmızı paket gönderiyor",
|
||||||
"taskNameRoomOwnerSendGiftUser": "Oda Sahibi Hediye Gönderir",
|
"taskNameRoomOwnerSendGiftUser": "Oda Sahibi Hediye Gönderir",
|
||||||
@ -585,7 +585,7 @@
|
|||||||
"taskNameRoomOwnerMicTime": "Oda Sahibi odada mikrofona geçer",
|
"taskNameRoomOwnerMicTime": "Oda Sahibi odada mikrofona geçer",
|
||||||
"taskNamePersonalActiveInRoom": "Başkalarının Odalarında Aktif Ol",
|
"taskNamePersonalActiveInRoom": "Başkalarının Odalarında Aktif Ol",
|
||||||
"taskNamePersonalGameConsume": "Oyun Harcaması",
|
"taskNamePersonalGameConsume": "Oyun Harcaması",
|
||||||
"taskNamePersonalMicInRoom": "Mikrofonu Aç",
|
"taskNamePersonalMicInRoom": "Mikrofona Çık",
|
||||||
"complete": "Tamamla",
|
"complete": "Tamamla",
|
||||||
"shareTo": "Paylaş",
|
"shareTo": "Paylaş",
|
||||||
"faceBook": "Facebook",
|
"faceBook": "Facebook",
|
||||||
@ -596,7 +596,7 @@
|
|||||||
"dailyCoinBonanzaRules": "Günlük Jeton Çılgınlığı Kuralları",
|
"dailyCoinBonanzaRules": "Günlük Jeton Çılgınlığı Kuralları",
|
||||||
"roomOwnerTasks": "Oda Sahibi Görevleri",
|
"roomOwnerTasks": "Oda Sahibi Görevleri",
|
||||||
"personalTasks": "Kişisel Görevler",
|
"personalTasks": "Kişisel Görevler",
|
||||||
"noPromptsToday": "Bugün hiçbir istem yok.",
|
"noPromptsToday": "Bugün görev yok.",
|
||||||
"getPaidToRefer": "Tavsiye Ederek Para Kazanın",
|
"getPaidToRefer": "Tavsiye Ederek Para Kazanın",
|
||||||
"theImageSizeCannotExceed": "Görsel boyutu 4M'yi geçemez",
|
"theImageSizeCannotExceed": "Görsel boyutu 4M'yi geçemez",
|
||||||
"activity": "Etkinlik",
|
"activity": "Etkinlik",
|
||||||
@ -630,7 +630,7 @@
|
|||||||
"confirmUseTips": "Kullanmak için onaylıyor musunuz?",
|
"confirmUseTips": "Kullanmak için onaylıyor musunuz?",
|
||||||
"pleaseUploadUserAvatar": "Lütfen bir profil fotoğrafı yükleyin.",
|
"pleaseUploadUserAvatar": "Lütfen bir profil fotoğrafı yükleyin.",
|
||||||
"joinMemberTips": "Eğer odada turist iseniz, mikroyu alamazsınız.",
|
"joinMemberTips": "Eğer odada turist iseniz, mikroyu alamazsınız.",
|
||||||
"giftGivingSuccessful": "Hediye verme başarılı.",
|
"giftGivingSuccessful": "Hediye başarıyla gönderildi.",
|
||||||
"theAccountPasswordCannotBeEmpty": "Hesap veya şifre boş olamaz.",
|
"theAccountPasswordCannotBeEmpty": "Hesap veya şifre boş olamaz.",
|
||||||
"invitesYouToTheMicrophone": "{1} seni mikroya davet ediyor",
|
"invitesYouToTheMicrophone": "{1} seni mikroya davet ediyor",
|
||||||
"english": "İngilizce",
|
"english": "İngilizce",
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ import 'package:yumi/services/audio/rtc_manager.dart';
|
|||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
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/ui_kit/widgets/countdown_timer.dart';
|
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/gift/sc_gift_combo_send_button.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||||||
import 'package:yumi/modules/wallet/wallet_route.dart';
|
import 'package:yumi/modules/wallet/wallet_route.dart';
|
||||||
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
||||||
@ -47,16 +50,18 @@ class _GiftPageTabItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GiftPage extends StatefulWidget {
|
class GiftPage extends StatefulWidget {
|
||||||
SocialChatUserProfile? toUser;
|
final SocialChatUserProfile? toUser;
|
||||||
|
|
||||||
GiftPage({super.key, this.toUser});
|
const GiftPage({super.key, this.toUser});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_GiftPageState createState() => _GiftPageState();
|
State<GiftPage> createState() => _GiftPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GiftPageState extends State<GiftPage>
|
class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
||||||
with SingleTickerProviderStateMixin {
|
static const Duration _comboFeedbackDuration = Duration(seconds: 3);
|
||||||
|
static const Duration _comboSendBatchWindow = Duration(milliseconds: 200);
|
||||||
|
|
||||||
static const List<String> _preferredGiftTabOrder = <String>[
|
static const List<String> _preferredGiftTabOrder = <String>[
|
||||||
"ALL",
|
"ALL",
|
||||||
"ACTIVITY",
|
"ACTIVITY",
|
||||||
@ -97,6 +102,12 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
|
|
||||||
int giftType = 0;
|
int giftType = 0;
|
||||||
Debouncer debouncer = Debouncer();
|
Debouncer debouncer = Debouncer();
|
||||||
|
late final AnimationController _comboFeedbackController;
|
||||||
|
final ListQueue<_PendingGiftSendBatch> _comboSendBatchQueue =
|
||||||
|
ListQueue<_PendingGiftSendBatch>();
|
||||||
|
Timer? _comboSendBatchTimer;
|
||||||
|
bool _isComboSendBatchInFlight = false;
|
||||||
|
bool _showComboFeedback = false;
|
||||||
|
|
||||||
void _giftFxLog(String message) {
|
void _giftFxLog(String message) {
|
||||||
debugPrint('[GiftFX][Send] $message');
|
debugPrint('[GiftFX][Send] $message');
|
||||||
@ -200,6 +211,16 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_comboFeedbackController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: _comboFeedbackDuration,
|
||||||
|
)..addStatusListener((status) {
|
||||||
|
if (status == AnimationStatus.completed && mounted) {
|
||||||
|
setState(() {
|
||||||
|
_showComboFeedback = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||||||
Provider.of<SCAppGeneralManager>(context, listen: false).giftList();
|
Provider.of<SCAppGeneralManager>(context, listen: false).giftList();
|
||||||
Provider.of<SCAppGeneralManager>(context, listen: false).giftActivityList();
|
Provider.of<SCAppGeneralManager>(context, listen: false).giftActivityList();
|
||||||
@ -224,8 +245,13 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_comboSendBatchTimer?.cancel();
|
||||||
|
if (_comboSendBatchQueue.isNotEmpty) {
|
||||||
|
unawaited(_flushAllPendingComboGiftSends());
|
||||||
|
}
|
||||||
_tabController?.removeListener(_handleTabChanged);
|
_tabController?.removeListener(_handleTabChanged);
|
||||||
_tabController?.dispose();
|
_tabController?.dispose();
|
||||||
|
_comboFeedbackController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,29 +807,7 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
size: 20.w,
|
size: 20.w,
|
||||||
),
|
),
|
||||||
SizedBox(width: 5.w),
|
SizedBox(width: 5.w),
|
||||||
GestureDetector(
|
_buildSendButton(),
|
||||||
onTap: () {
|
|
||||||
giveGifts();
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: 8.w,
|
|
||||||
horizontal: 20.w,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
SocialChatTheme.primaryLight,
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(5),
|
|
||||||
),
|
|
||||||
child: text(
|
|
||||||
SCAppLocalizations.of(
|
|
||||||
context,
|
|
||||||
)!.send,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1068,8 +1072,7 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///赠送礼物
|
_GiftSendRequest? _buildGiftSendRequest() {
|
||||||
void giveGifts() async {
|
|
||||||
List<String> acceptUserIds = [];
|
List<String> acceptUserIds = [];
|
||||||
List<MicRes> acceptUsers = [];
|
List<MicRes> acceptUsers = [];
|
||||||
|
|
||||||
@ -1102,7 +1105,7 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
'giveType=$giveType',
|
'giveType=$giveType',
|
||||||
);
|
);
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.pleaseSelectTheRecipient);
|
SCTts.show(SCAppLocalizations.of(context)!.pleaseSelectTheRecipient);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
final selectedGift = checkedGift;
|
final selectedGift = checkedGift;
|
||||||
if (selectedGift == null) {
|
if (selectedGift == null) {
|
||||||
@ -1111,41 +1114,18 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
'acceptUserIds=${acceptUserIds.join(",")} '
|
'acceptUserIds=${acceptUserIds.join(",")} '
|
||||||
'giveType=$giveType',
|
'giveType=$giveType',
|
||||||
);
|
);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
final selectedNumber = number;
|
final selectedNumber = number;
|
||||||
final roomId = rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
final roomId = rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||||
final roomAccount =
|
final roomAccount =
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "";
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "";
|
||||||
final isLuckyGiftRequest = _usesLuckyGiftEndpoint(selectedGift);
|
final isLuckyGiftRequest = _usesLuckyGiftEndpoint(selectedGift);
|
||||||
final requestName = isLuckyGiftRequest ? 'giveLuckyGift' : 'giveGift';
|
|
||||||
final senderId = AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
|
|
||||||
final senderName =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.userNickname ?? "";
|
|
||||||
final stopwatch = Stopwatch()..start();
|
|
||||||
|
|
||||||
_giftFxLog(
|
|
||||||
'tap send start request=$requestName '
|
|
||||||
'senderId=$senderId '
|
|
||||||
'senderName=$senderName '
|
|
||||||
'giftId=${selectedGift.id} '
|
|
||||||
'giftName=${selectedGift.giftName} '
|
|
||||||
'giftTab=${selectedGift.giftTab} '
|
|
||||||
'special=${selectedGift.special} '
|
|
||||||
'standardId=${selectedGift.standardId} '
|
|
||||||
'giftCandy=${selectedGift.giftCandy} '
|
|
||||||
'number=$selectedNumber '
|
|
||||||
'acceptCount=${acceptUserIds.length} '
|
|
||||||
'acceptUserIds=${acceptUserIds.join(",")} '
|
|
||||||
'roomId=$roomId '
|
|
||||||
'roomAccount=$roomAccount '
|
|
||||||
'giveType=$giveType '
|
|
||||||
'giftType=$giftType',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isLuckyGiftRequest && !_hasValidLuckyGiftStandardId(selectedGift)) {
|
if (isLuckyGiftRequest && !_hasValidLuckyGiftStandardId(selectedGift)) {
|
||||||
const configError =
|
const configError =
|
||||||
'Gift configuration unavailable, please try another gift.';
|
'Gift configuration unavailable, please try another gift.';
|
||||||
|
final requestName = isLuckyGiftRequest ? 'giveLuckyGift' : 'giveGift';
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'$requestName skipped giftId=${selectedGift.id} '
|
'$requestName skipped giftId=${selectedGift.id} '
|
||||||
'giftName=${selectedGift.giftName} '
|
'giftName=${selectedGift.giftName} '
|
||||||
@ -1154,84 +1134,248 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
'reason=invalid_standard_id',
|
'reason=invalid_standard_id',
|
||||||
);
|
);
|
||||||
SCTts.show(configError);
|
SCTts.show(configError);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _GiftSendRequest(
|
||||||
|
acceptUserIds: acceptUserIds,
|
||||||
|
acceptUsers: acceptUsers,
|
||||||
|
gift: selectedGift,
|
||||||
|
quantity: selectedNumber,
|
||||||
|
roomId: roomId,
|
||||||
|
roomAccount: roomAccount,
|
||||||
|
isLuckyGiftRequest: isLuckyGiftRequest,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
///赠送礼物
|
||||||
|
void giveGifts() async {
|
||||||
|
final request = _buildGiftSendRequest();
|
||||||
|
if (request == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_activateComboFeedback(
|
||||||
|
gift: request.gift,
|
||||||
|
quantity: request.quantity,
|
||||||
|
acceptUserIds: request.acceptUserIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_supportsComboRequestBatching(request.gift)) {
|
||||||
|
_enqueueComboGiftSendRequest(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _performGiftSend(request, trigger: 'direct');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _supportsComboRequestBatching(SocialChatGiftRes gift) {
|
||||||
|
return _supportsComboFeedback(gift);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _enqueueComboGiftSendRequest(_GiftSendRequest request) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
_PendingGiftSendBatch? existingBatch;
|
||||||
|
for (final batch in _comboSendBatchQueue) {
|
||||||
|
if (batch.batchKey == request.batchKey) {
|
||||||
|
existingBatch = batch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingBatch != null) {
|
||||||
|
existingBatch.quantity += request.quantity;
|
||||||
|
existingBatch.readyAt = now.add(_comboSendBatchWindow);
|
||||||
|
_giftFxLog(
|
||||||
|
'aggregate combo send update '
|
||||||
|
'batchKey=${existingBatch.batchKey} '
|
||||||
|
'giftId=${request.gift.id} '
|
||||||
|
'quantity=${existingBatch.quantity} '
|
||||||
|
'acceptUserIds=${request.acceptUserIds.join(",")}',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final batch = _PendingGiftSendBatch.fromRequest(
|
||||||
|
request,
|
||||||
|
readyAt: now.add(_comboSendBatchWindow),
|
||||||
|
);
|
||||||
|
_comboSendBatchQueue.add(batch);
|
||||||
|
_giftFxLog(
|
||||||
|
'aggregate combo send enqueue '
|
||||||
|
'batchKey=${batch.batchKey} '
|
||||||
|
'giftId=${request.gift.id} '
|
||||||
|
'quantity=${batch.quantity} '
|
||||||
|
'acceptUserIds=${request.acceptUserIds.join(",")}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scheduleNextComboGiftSendFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleNextComboGiftSendFlush() {
|
||||||
|
_comboSendBatchTimer?.cancel();
|
||||||
|
_comboSendBatchTimer = null;
|
||||||
|
if (_isComboSendBatchInFlight || _comboSendBatchQueue.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final headBatch = _comboSendBatchQueue.first;
|
||||||
|
final delay = headBatch.readyAt.difference(DateTime.now());
|
||||||
|
if (delay <= Duration.zero) {
|
||||||
|
unawaited(_flushNextComboGiftSendBatch());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_comboSendBatchTimer = Timer(delay, () {
|
||||||
|
unawaited(_flushNextComboGiftSendBatch());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _flushNextComboGiftSendBatch() async {
|
||||||
|
_comboSendBatchTimer?.cancel();
|
||||||
|
_comboSendBatchTimer = null;
|
||||||
|
if (_isComboSendBatchInFlight || _comboSendBatchQueue.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final headBatch = _comboSendBatchQueue.first;
|
||||||
|
if (headBatch.readyAt.isAfter(DateTime.now())) {
|
||||||
|
_scheduleNextComboGiftSendFlush();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_comboSendBatchQueue.removeFirst();
|
||||||
|
_isComboSendBatchInFlight = true;
|
||||||
|
try {
|
||||||
|
await _performGiftSend(headBatch.toRequest(), trigger: 'batched');
|
||||||
|
} finally {
|
||||||
|
_isComboSendBatchInFlight = false;
|
||||||
|
_scheduleNextComboGiftSendFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _flushAllPendingComboGiftSends() async {
|
||||||
|
_comboSendBatchTimer?.cancel();
|
||||||
|
_comboSendBatchTimer = null;
|
||||||
|
while (_comboSendBatchQueue.isNotEmpty) {
|
||||||
|
if (_isComboSendBatchInFlight) {
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_comboSendBatchQueue.first.readyAt = DateTime.now();
|
||||||
|
await _flushNextComboGiftSendBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _performGiftSend(
|
||||||
|
_GiftSendRequest request, {
|
||||||
|
required String trigger,
|
||||||
|
}) async {
|
||||||
|
final requestName = request.requestName;
|
||||||
|
final profileManager =
|
||||||
|
navigatorKey.currentState == null
|
||||||
|
? null
|
||||||
|
: Provider.of<SocialChatUserProfileManager>(
|
||||||
|
navigatorKey.currentState!.context,
|
||||||
|
listen: false,
|
||||||
|
);
|
||||||
|
final senderId = AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
|
||||||
|
final senderName =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.userNickname ?? "";
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
|
_giftFxLog(
|
||||||
|
'send start trigger=$trigger request=$requestName '
|
||||||
|
'senderId=$senderId '
|
||||||
|
'senderName=$senderName '
|
||||||
|
'giftId=${request.gift.id} '
|
||||||
|
'giftName=${request.gift.giftName} '
|
||||||
|
'giftTab=${request.gift.giftTab} '
|
||||||
|
'special=${request.gift.special} '
|
||||||
|
'standardId=${request.gift.standardId} '
|
||||||
|
'giftCandy=${request.gift.giftCandy} '
|
||||||
|
'number=${request.quantity} '
|
||||||
|
'acceptCount=${request.acceptUserIds.length} '
|
||||||
|
'acceptUserIds=${request.acceptUserIds.join(",")} '
|
||||||
|
'roomId=${request.roomId} '
|
||||||
|
'roomAccount=${request.roomAccount} '
|
||||||
|
'giveType=$giveType '
|
||||||
|
'giftType=$giftType',
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final repository = SCChatRoomRepository();
|
final repository = SCChatRoomRepository();
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'calling repository.$requestName '
|
'calling repository.$requestName '
|
||||||
'giftId=${selectedGift.id} '
|
'trigger=$trigger '
|
||||||
'roomId=$roomId '
|
'giftId=${request.gift.id} '
|
||||||
'acceptUserIds=${acceptUserIds.join(",")} '
|
'roomId=${request.roomId} '
|
||||||
'quantity=$selectedNumber',
|
'acceptUserIds=${request.acceptUserIds.join(",")} '
|
||||||
|
'quantity=${request.quantity}',
|
||||||
);
|
);
|
||||||
final result =
|
final result =
|
||||||
isLuckyGiftRequest
|
request.isLuckyGiftRequest
|
||||||
? await repository.giveLuckyGift(
|
? await repository.giveLuckyGift(
|
||||||
acceptUserIds,
|
request.acceptUserIds,
|
||||||
selectedGift.id ?? "",
|
request.gift.id ?? "",
|
||||||
selectedNumber,
|
request.quantity,
|
||||||
false,
|
false,
|
||||||
roomId: roomId,
|
roomId: request.roomId,
|
||||||
)
|
)
|
||||||
: await repository.giveGift(
|
: await repository.giveGift(
|
||||||
acceptUserIds,
|
request.acceptUserIds,
|
||||||
selectedGift.id ?? "",
|
request.gift.id ?? "",
|
||||||
selectedNumber,
|
request.quantity,
|
||||||
false,
|
false,
|
||||||
roomId: roomId,
|
roomId: request.roomId,
|
||||||
);
|
);
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'$requestName success giftId=${selectedGift.id} '
|
'$requestName success trigger=$trigger '
|
||||||
'giftName=${selectedGift.giftName} '
|
'giftId=${request.gift.id} '
|
||||||
'giftSourceUrl=${selectedGift.giftSourceUrl} '
|
'giftName=${request.gift.giftName} '
|
||||||
'special=${selectedGift.special} '
|
'giftSourceUrl=${request.gift.giftSourceUrl} '
|
||||||
'giftTab=${selectedGift.giftTab} '
|
'special=${request.gift.special} '
|
||||||
'standardId=${selectedGift.standardId} '
|
'giftTab=${request.gift.giftTab} '
|
||||||
'number=$selectedNumber '
|
'standardId=${request.gift.standardId} '
|
||||||
'acceptUserIds=${acceptUserIds.join(",")} '
|
'number=${request.quantity} '
|
||||||
'roomId=$roomId '
|
'acceptUserIds=${request.acceptUserIds.join(",")} '
|
||||||
|
'roomId=${request.roomId} '
|
||||||
'balance=$result '
|
'balance=$result '
|
||||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||||
);
|
);
|
||||||
// SCTts.show(SCAppLocalizations.of(context)!.giftGivingSuccessful);
|
if (request.isLuckyGiftRequest) {
|
||||||
if (isLuckyGiftRequest) {
|
|
||||||
_showLocalLuckyGiftFeedback(
|
_showLocalLuckyGiftFeedback(
|
||||||
acceptUsers,
|
request.acceptUsers,
|
||||||
gift: selectedGift,
|
gift: request.gift,
|
||||||
quantity: selectedNumber,
|
quantity: request.quantity,
|
||||||
);
|
);
|
||||||
await sendLuckGiftAnimOtherMsg(
|
await sendLuckGiftAnimOtherMsg(
|
||||||
acceptUsers,
|
request.acceptUsers,
|
||||||
gift: selectedGift,
|
gift: request.gift,
|
||||||
quantity: selectedNumber,
|
quantity: request.quantity,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
sendGiftMsg(acceptUsers, gift: selectedGift, quantity: selectedNumber);
|
sendGiftMsg(
|
||||||
}
|
request.acceptUsers,
|
||||||
if (mounted) {
|
gift: request.gift,
|
||||||
Provider.of<SocialChatUserProfileManager>(
|
quantity: request.quantity,
|
||||||
context,
|
);
|
||||||
listen: false,
|
|
||||||
).updateBalance(result);
|
|
||||||
}
|
}
|
||||||
|
profileManager?.updateBalance(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final errorMessage = _resolveGiftSendErrorMessage(e);
|
final errorMessage = _resolveGiftSendErrorMessage(e);
|
||||||
final errorDetails = _describeGiftSendError(e);
|
final errorDetails = _describeGiftSendError(e);
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'$requestName failed giftId=${selectedGift.id} '
|
'$requestName failed trigger=$trigger '
|
||||||
'giftName=${selectedGift.giftName} '
|
'giftId=${request.gift.id} '
|
||||||
'giftTab=${selectedGift.giftTab} '
|
'giftName=${request.gift.giftName} '
|
||||||
'standardId=${selectedGift.standardId} '
|
'giftTab=${request.gift.giftTab} '
|
||||||
|
'standardId=${request.gift.standardId} '
|
||||||
'error=$e '
|
'error=$e '
|
||||||
'resolvedError=$errorMessage '
|
'resolvedError=$errorMessage '
|
||||||
'details={$errorDetails} '
|
'details={$errorDetails} '
|
||||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||||
);
|
);
|
||||||
if (mounted) {
|
SCTts.show(errorMessage);
|
||||||
SCTts.show(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,6 +1491,39 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _activateComboFeedback({
|
||||||
|
required SocialChatGiftRes gift,
|
||||||
|
required int quantity,
|
||||||
|
required List<String> acceptUserIds,
|
||||||
|
}) {
|
||||||
|
if (!_supportsComboFeedback(gift)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_showComboFeedback = true;
|
||||||
|
});
|
||||||
|
_comboFeedbackController.forward(from: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _supportsComboFeedback(SocialChatGiftRes gift) {
|
||||||
|
final giftTab = (gift.giftTab ?? '').trim();
|
||||||
|
return giftTab == "LUCK" ||
|
||||||
|
giftTab == SCGiftType.LUCKY_GIFT.name ||
|
||||||
|
giftTab == SCGiftType.CP.name ||
|
||||||
|
giftTab == SCGiftType.MAGIC.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSendButton() {
|
||||||
|
return SCGiftComboSendButton(
|
||||||
|
label: SCAppLocalizations.of(context)!.send,
|
||||||
|
onPressed: giveGifts,
|
||||||
|
showCountdown: _showComboFeedback,
|
||||||
|
countdownAnimation: _comboFeedbackController,
|
||||||
|
width: 96.w,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 将数字giftType转换为字符串类型,用于活动礼物头部背景
|
/// 将数字giftType转换为字符串类型,用于活动礼物头部背景
|
||||||
String _giftTypeToString(int giftType) {
|
String _giftTypeToString(int giftType) {
|
||||||
switch (giftType) {
|
switch (giftType) {
|
||||||
@ -1364,7 +1541,11 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildGiftHead() {
|
_buildGiftHead() {
|
||||||
if (giftType == 1 || giftType == 2 || giftType == 3 || giftType == 5) {
|
if (giftType == 2 || giftType == 3) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (giftType == 1 || giftType == 5) {
|
||||||
// 获取基础路径
|
// 获取基础路径
|
||||||
String basePath = _strategy.getGiftPageActivityGiftHeadBackground(
|
String basePath = _strategy.getGiftPageActivityGiftHeadBackground(
|
||||||
_giftTypeToString(giftType),
|
_giftTypeToString(giftType),
|
||||||
@ -1375,15 +1556,15 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
if (SCGlobalConfig.lang == "ar") {
|
if (SCGlobalConfig.lang == "ar") {
|
||||||
// 移除扩展名,添加 _ar 后缀,然后重新添加扩展名
|
// 移除扩展名,添加 _ar 后缀,然后重新添加扩展名
|
||||||
if (basePath.endsWith('.png')) {
|
if (basePath.endsWith('.png')) {
|
||||||
imagePath = basePath.substring(0, basePath.length - 4) + '_ar.png';
|
imagePath = '${basePath.substring(0, basePath.length - 4)}_ar.png';
|
||||||
} else {
|
} else {
|
||||||
imagePath = basePath + '_ar';
|
imagePath = '${basePath}_ar';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (basePath.endsWith('.png')) {
|
if (basePath.endsWith('.png')) {
|
||||||
imagePath = basePath.substring(0, basePath.length - 4) + '_en.png';
|
imagePath = '${basePath.substring(0, basePath.length - 4)}_en.png';
|
||||||
} else {
|
} else {
|
||||||
imagePath = basePath + '_en';
|
imagePath = '${basePath}_en';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1446,14 +1627,12 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CheckNumber extends StatelessWidget {
|
class CheckNumber extends StatelessWidget {
|
||||||
final Function(int) onNumberChanged;
|
final ValueChanged<int> onNumberChanged;
|
||||||
late BuildContext context;
|
|
||||||
|
|
||||||
CheckNumber({super.key, required this.onNumberChanged});
|
const CheckNumber({super.key, required this.onNumberChanged});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
this.context = context;
|
|
||||||
return Container(
|
return Container(
|
||||||
alignment: AlignmentDirectional.topEnd,
|
alignment: AlignmentDirectional.topEnd,
|
||||||
margin: EdgeInsets.only(right: width(22)),
|
margin: EdgeInsets.only(right: width(22)),
|
||||||
@ -1516,6 +1695,89 @@ class CheckNumber extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _GiftSendRequest {
|
||||||
|
const _GiftSendRequest({
|
||||||
|
required this.acceptUserIds,
|
||||||
|
required this.acceptUsers,
|
||||||
|
required this.gift,
|
||||||
|
required this.quantity,
|
||||||
|
required this.roomId,
|
||||||
|
required this.roomAccount,
|
||||||
|
required this.isLuckyGiftRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<String> acceptUserIds;
|
||||||
|
final List<MicRes> acceptUsers;
|
||||||
|
final SocialChatGiftRes gift;
|
||||||
|
final int quantity;
|
||||||
|
final String roomId;
|
||||||
|
final String roomAccount;
|
||||||
|
final bool isLuckyGiftRequest;
|
||||||
|
|
||||||
|
String get requestName => isLuckyGiftRequest ? 'giveLuckyGift' : 'giveGift';
|
||||||
|
|
||||||
|
String get batchKey {
|
||||||
|
final sortedAcceptUserIds = List<String>.from(acceptUserIds)..sort();
|
||||||
|
return '${isLuckyGiftRequest ? "lucky" : "gift"}'
|
||||||
|
'|${gift.id ?? ""}'
|
||||||
|
'|$roomId'
|
||||||
|
'|${sortedAcceptUserIds.join(",")}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PendingGiftSendBatch {
|
||||||
|
_PendingGiftSendBatch({
|
||||||
|
required this.batchKey,
|
||||||
|
required this.acceptUserIds,
|
||||||
|
required this.acceptUsers,
|
||||||
|
required this.gift,
|
||||||
|
required this.quantity,
|
||||||
|
required this.roomId,
|
||||||
|
required this.roomAccount,
|
||||||
|
required this.isLuckyGiftRequest,
|
||||||
|
required this.readyAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory _PendingGiftSendBatch.fromRequest(
|
||||||
|
_GiftSendRequest request, {
|
||||||
|
required DateTime readyAt,
|
||||||
|
}) {
|
||||||
|
return _PendingGiftSendBatch(
|
||||||
|
batchKey: request.batchKey,
|
||||||
|
acceptUserIds: List<String>.from(request.acceptUserIds),
|
||||||
|
acceptUsers: List<MicRes>.from(request.acceptUsers),
|
||||||
|
gift: request.gift,
|
||||||
|
quantity: request.quantity,
|
||||||
|
roomId: request.roomId,
|
||||||
|
roomAccount: request.roomAccount,
|
||||||
|
isLuckyGiftRequest: request.isLuckyGiftRequest,
|
||||||
|
readyAt: readyAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String batchKey;
|
||||||
|
final List<String> acceptUserIds;
|
||||||
|
final List<MicRes> acceptUsers;
|
||||||
|
final SocialChatGiftRes gift;
|
||||||
|
int quantity;
|
||||||
|
final String roomId;
|
||||||
|
final String roomAccount;
|
||||||
|
final bool isLuckyGiftRequest;
|
||||||
|
DateTime readyAt;
|
||||||
|
|
||||||
|
_GiftSendRequest toRequest() {
|
||||||
|
return _GiftSendRequest(
|
||||||
|
acceptUserIds: List<String>.from(acceptUserIds),
|
||||||
|
acceptUsers: List<MicRes>.from(acceptUsers),
|
||||||
|
gift: gift,
|
||||||
|
quantity: quantity,
|
||||||
|
roomId: roomId,
|
||||||
|
roomAccount: roomAccount,
|
||||||
|
isLuckyGiftRequest: isLuckyGiftRequest,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HeadSelect {
|
class HeadSelect {
|
||||||
bool isSelect = false;
|
bool isSelect = false;
|
||||||
MicRes? mic;
|
MicRes? mic;
|
||||||
|
|||||||
@ -1,149 +1,151 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
|
||||||
|
class AllChatPage extends StatefulWidget {
|
||||||
class AllChatPage extends StatefulWidget {
|
const AllChatPage({super.key});
|
||||||
@override
|
|
||||||
_AllChatPageState createState() => _AllChatPageState();
|
@override
|
||||||
}
|
State<AllChatPage> createState() => _AllChatPageState();
|
||||||
|
}
|
||||||
class _AllChatPageState extends State<AllChatPage> {
|
|
||||||
ScrollController _controller = ScrollController();
|
class _AllChatPageState extends State<AllChatPage> {
|
||||||
bool showGoBottom = true;
|
final ScrollController _controller = ScrollController();
|
||||||
bool _isDisposed = false;
|
bool showGoBottom = true;
|
||||||
List<Msg> _msgList = [];
|
bool _isDisposed = false;
|
||||||
late RtmProvider provider;
|
final List<Msg> _msgList = [];
|
||||||
|
late RtmProvider provider;
|
||||||
@override
|
|
||||||
void initState() {
|
@override
|
||||||
super.initState();
|
void initState() {
|
||||||
_controller.addListener(_scrollListener);
|
super.initState();
|
||||||
provider = Provider.of<RtmProvider>(context, listen: false);
|
_controller.addListener(_scrollListener);
|
||||||
var msgList = provider.roomAllMsgList;
|
provider = Provider.of<RtmProvider>(context, listen: false);
|
||||||
provider.msgAllListener = _onNewMsg;
|
final msgList = provider.roomAllMsgList;
|
||||||
_msgList.addAll(msgList ??= []);
|
provider.msgAllListener = _onNewMsg;
|
||||||
// 使用安全的方式初始化滚动位置
|
_msgList.addAll(msgList);
|
||||||
_initScrollPosition();
|
// 使用安全的方式初始化滚动位置
|
||||||
}
|
_initScrollPosition();
|
||||||
|
}
|
||||||
void _scrollListener() {
|
|
||||||
final position = _controller.position;
|
void _scrollListener() {
|
||||||
|
final position = _controller.position;
|
||||||
// 容差范围(5像素)避免精度问题
|
|
||||||
const tolerance = 10.0;
|
// 容差范围(5像素)避免精度问题
|
||||||
|
const tolerance = 10.0;
|
||||||
// 判断是否在第一个项目(颠倒列表的底部)
|
|
||||||
final isAtBottom = position.pixels < position.minScrollExtent + tolerance;
|
// 判断是否在第一个项目(颠倒列表的底部)
|
||||||
|
final isAtBottom = position.pixels < position.minScrollExtent + tolerance;
|
||||||
// 判断是否在最后一个项目(颠倒列表的顶部)
|
|
||||||
// final isAtTop = position.pixels >= position.maxScrollExtent - tolerance;
|
// 判断是否在最后一个项目(颠倒列表的顶部)
|
||||||
if (isAtBottom != showGoBottom) {
|
// final isAtTop = position.pixels >= position.maxScrollExtent - tolerance;
|
||||||
showGoBottom = isAtBottom;
|
if (isAtBottom != showGoBottom) {
|
||||||
if (!_isDisposed) {
|
showGoBottom = isAtBottom;
|
||||||
setState(() {});
|
if (!_isDisposed) {
|
||||||
}
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 安全初始化滚动位置
|
|
||||||
void _initScrollPosition() {
|
// 安全初始化滚动位置
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
void _initScrollPosition() {
|
||||||
if (_isDisposed) return;
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
_controller.jumpTo(0.0);
|
if (_isDisposed) return;
|
||||||
});
|
_controller.jumpTo(0.0);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
_onNewMsg(Msg msg) {
|
|
||||||
if (_isDisposed) return; // 如果组件已销毁,直接返回
|
_onNewMsg(Msg msg) {
|
||||||
if (msg.groupId == "-1000") {
|
if (_isDisposed) return; // 如果组件已销毁,直接返回
|
||||||
///清屏
|
if (msg.groupId == "-1000") {
|
||||||
_msgList.clear();
|
///清屏
|
||||||
setState(() {});
|
_msgList.clear();
|
||||||
return;
|
setState(() {});
|
||||||
}
|
return;
|
||||||
if (!_msgList.contains(msg)) {
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_msgList.insert(0, msg);
|
if (_msgList.contains(msg)) {
|
||||||
});
|
_msgList.remove(msg);
|
||||||
_controller.jumpTo(0.0);
|
}
|
||||||
}
|
_msgList.insert(0, msg);
|
||||||
}
|
});
|
||||||
|
_controller.jumpTo(0.0);
|
||||||
@override
|
}
|
||||||
void dispose() {
|
|
||||||
_isDisposed = true;
|
@override
|
||||||
provider.msgAllListener = null;
|
void dispose() {
|
||||||
_controller.removeListener(_scrollListener);
|
_isDisposed = true;
|
||||||
_controller.dispose();
|
provider.msgAllListener = null;
|
||||||
super.dispose();
|
_controller.removeListener(_scrollListener);
|
||||||
}
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
@override
|
}
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
@override
|
||||||
backgroundColor: Colors.transparent,
|
Widget build(BuildContext context) {
|
||||||
resizeToAvoidBottomInset: false,
|
return Scaffold(
|
||||||
body: Stack(
|
backgroundColor: Colors.transparent,
|
||||||
alignment: Alignment.bottomLeft,
|
resizeToAvoidBottomInset: false,
|
||||||
children: [
|
body: Stack(
|
||||||
ListView.builder(
|
alignment: Alignment.bottomLeft,
|
||||||
reverse: true,
|
children: [
|
||||||
controller: _controller,
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
reverse: true,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
controller: _controller,
|
||||||
return MsgItem(
|
shrinkWrap: true,
|
||||||
msg: _msgList[index],
|
itemBuilder: (BuildContext context, int index) {
|
||||||
onClick: (SocialChatUserProfile? user) {
|
return MsgItem(
|
||||||
Provider.of<RtcProvider>(context, listen: false).clickSite(
|
msg: _msgList[index],
|
||||||
Provider.of<RtcProvider>(
|
onClick: (SocialChatUserProfile? user) {
|
||||||
context,
|
Provider.of<RtcProvider>(context, listen: false).clickSite(
|
||||||
listen: false,
|
Provider.of<RtcProvider>(
|
||||||
).userOnMaiInIndex(user?.id ?? ""),
|
context,
|
||||||
clickUser: user,
|
listen: false,
|
||||||
);
|
).userOnMaiInIndex(user?.id ?? ""),
|
||||||
},
|
clickUser: user,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: _msgList.length,
|
);
|
||||||
),
|
},
|
||||||
Visibility(
|
itemCount: _msgList.length,
|
||||||
visible: !showGoBottom,
|
),
|
||||||
child: GestureDetector(
|
Visibility(
|
||||||
onTap: () {
|
visible: !showGoBottom,
|
||||||
if (_controller.hasClients) {
|
child: GestureDetector(
|
||||||
_controller.jumpTo(0.0);
|
onTap: () {
|
||||||
}
|
if (_controller.hasClients) {
|
||||||
},
|
_controller.jumpTo(0.0);
|
||||||
child: Container(
|
}
|
||||||
margin: EdgeInsets.symmetric(horizontal: width(15)),
|
},
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
margin: EdgeInsets.symmetric(horizontal: width(15)),
|
||||||
borderRadius: BorderRadius.circular(52),
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
||||||
color: Colors.white,
|
decoration: BoxDecoration(
|
||||||
),
|
borderRadius: BorderRadius.circular(52),
|
||||||
child: Row(
|
color: Colors.white,
|
||||||
mainAxisSize: MainAxisSize.min,
|
),
|
||||||
children: <Widget>[
|
child: Row(
|
||||||
Text(
|
mainAxisSize: MainAxisSize.min,
|
||||||
SCAppLocalizations.of(context)!.scrollToTheBottom,
|
children: <Widget>[
|
||||||
style: TextStyle(fontSize: 10.sp),
|
Text(
|
||||||
),
|
SCAppLocalizations.of(context)!.scrollToTheBottom,
|
||||||
SizedBox(width: 4.w),
|
style: TextStyle(fontSize: 10.sp),
|
||||||
Icon(Icons.chevron_right, size: 10.w),
|
),
|
||||||
],
|
SizedBox(width: 4.w),
|
||||||
),
|
Icon(Icons.chevron_right, size: 10.w),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
}
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,141 +1,143 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
|
||||||
|
class GiftChatPage extends StatefulWidget {
|
||||||
class GiftChatPage extends StatefulWidget {
|
const GiftChatPage({super.key});
|
||||||
@override
|
|
||||||
_GiftChatPageState createState() => _GiftChatPageState();
|
@override
|
||||||
}
|
State<GiftChatPage> createState() => _GiftChatPageState();
|
||||||
|
}
|
||||||
class _GiftChatPageState extends State<GiftChatPage> {
|
|
||||||
ScrollController _controller = ScrollController();
|
class _GiftChatPageState extends State<GiftChatPage> {
|
||||||
bool showGoBottom = true;
|
final ScrollController _controller = ScrollController();
|
||||||
bool _isDisposed = false;
|
bool showGoBottom = true;
|
||||||
List<Msg> _msgList = [];
|
bool _isDisposed = false;
|
||||||
late RtmProvider provider;
|
final List<Msg> _msgList = [];
|
||||||
|
late RtmProvider provider;
|
||||||
@override
|
|
||||||
void initState() {
|
@override
|
||||||
super.initState();
|
void initState() {
|
||||||
_controller.addListener(_scrollListener);
|
super.initState();
|
||||||
provider = Provider.of<RtmProvider>(context, listen: false);
|
_controller.addListener(_scrollListener);
|
||||||
var msgList = provider.roomGiftMsgList;
|
provider = Provider.of<RtmProvider>(context, listen: false);
|
||||||
provider.msgGiftListener = _onNewMsg;
|
final msgList = provider.roomGiftMsgList;
|
||||||
_msgList.addAll(msgList ??= []);
|
provider.msgGiftListener = _onNewMsg;
|
||||||
// 使用安全的方式初始化滚动位置
|
_msgList.addAll(msgList);
|
||||||
_initScrollPosition();
|
// 使用安全的方式初始化滚动位置
|
||||||
}
|
_initScrollPosition();
|
||||||
|
}
|
||||||
void _scrollListener() {
|
|
||||||
final position = _controller.position;
|
void _scrollListener() {
|
||||||
|
final position = _controller.position;
|
||||||
// 容差范围(5像素)避免精度问题
|
|
||||||
const tolerance = 10.0;
|
// 容差范围(5像素)避免精度问题
|
||||||
|
const tolerance = 10.0;
|
||||||
// 判断是否在第一个项目(颠倒列表的底部)
|
|
||||||
final isAtBottom = position.pixels < position.minScrollExtent + tolerance;
|
// 判断是否在第一个项目(颠倒列表的底部)
|
||||||
|
final isAtBottom = position.pixels < position.minScrollExtent + tolerance;
|
||||||
// 判断是否在最后一个项目(颠倒列表的顶部)
|
|
||||||
// final isAtTop = position.pixels >= position.maxScrollExtent - tolerance;
|
// 判断是否在最后一个项目(颠倒列表的顶部)
|
||||||
if (isAtBottom != showGoBottom) {
|
// final isAtTop = position.pixels >= position.maxScrollExtent - tolerance;
|
||||||
showGoBottom = isAtBottom;
|
if (isAtBottom != showGoBottom) {
|
||||||
if (!_isDisposed) {
|
showGoBottom = isAtBottom;
|
||||||
setState(() {});
|
if (!_isDisposed) {
|
||||||
}
|
setState(() {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 安全初始化滚动位置
|
|
||||||
void _initScrollPosition() {
|
// 安全初始化滚动位置
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
void _initScrollPosition() {
|
||||||
if (_isDisposed) return;
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
_controller.jumpTo(0.0);
|
if (_isDisposed) return;
|
||||||
});
|
_controller.jumpTo(0.0);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
_onNewMsg(Msg msg) {
|
|
||||||
if (_isDisposed) return; // 如果组件已销毁,直接返回
|
_onNewMsg(Msg msg) {
|
||||||
if (msg.groupId == "-1000") {
|
if (_isDisposed) return; // 如果组件已销毁,直接返回
|
||||||
///清屏
|
if (msg.groupId == "-1000") {
|
||||||
_msgList.clear();
|
///清屏
|
||||||
setState(() {});
|
_msgList.clear();
|
||||||
return;
|
setState(() {});
|
||||||
}
|
return;
|
||||||
if (!_msgList.contains(msg)) {
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_msgList.insert(0, msg);
|
if (_msgList.contains(msg)) {
|
||||||
});
|
_msgList.remove(msg);
|
||||||
_controller.jumpTo(0.0);
|
}
|
||||||
}
|
_msgList.insert(0, msg);
|
||||||
}
|
});
|
||||||
|
_controller.jumpTo(0.0);
|
||||||
@override
|
}
|
||||||
void dispose() {
|
|
||||||
_isDisposed = true;
|
@override
|
||||||
provider.msgGiftListener = null;
|
void dispose() {
|
||||||
_controller.removeListener(_scrollListener);
|
_isDisposed = true;
|
||||||
_controller.dispose();
|
provider.msgGiftListener = null;
|
||||||
super.dispose();
|
_controller.removeListener(_scrollListener);
|
||||||
}
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
@override
|
}
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
@override
|
||||||
backgroundColor: Colors.transparent,
|
Widget build(BuildContext context) {
|
||||||
resizeToAvoidBottomInset: false,
|
return Scaffold(
|
||||||
body: Stack(
|
backgroundColor: Colors.transparent,
|
||||||
alignment: Alignment.bottomLeft,
|
resizeToAvoidBottomInset: false,
|
||||||
children: [
|
body: Stack(
|
||||||
ListView.builder(
|
alignment: Alignment.bottomLeft,
|
||||||
reverse: true,
|
children: [
|
||||||
controller: _controller,
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
reverse: true,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
controller: _controller,
|
||||||
return MsgItem(
|
shrinkWrap: true,
|
||||||
msg: _msgList[index],
|
itemBuilder: (BuildContext context, int index) {
|
||||||
onClick: (SocialChatUserProfile? user) {},
|
return MsgItem(
|
||||||
);
|
msg: _msgList[index],
|
||||||
},
|
onClick: (SocialChatUserProfile? user) {},
|
||||||
itemCount: _msgList.length,
|
);
|
||||||
),
|
},
|
||||||
Visibility(
|
itemCount: _msgList.length,
|
||||||
visible: !showGoBottom,
|
),
|
||||||
child: GestureDetector(
|
Visibility(
|
||||||
onTap: () {
|
visible: !showGoBottom,
|
||||||
if (_controller.hasClients) {
|
child: GestureDetector(
|
||||||
_controller.jumpTo(0.0);
|
onTap: () {
|
||||||
}
|
if (_controller.hasClients) {
|
||||||
},
|
_controller.jumpTo(0.0);
|
||||||
child: Container(
|
}
|
||||||
margin: EdgeInsets.symmetric(horizontal: width(15)),
|
},
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
margin: EdgeInsets.symmetric(horizontal: width(15)),
|
||||||
borderRadius: BorderRadius.circular(52),
|
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.w),
|
||||||
color: Colors.white,
|
decoration: BoxDecoration(
|
||||||
),
|
borderRadius: BorderRadius.circular(52),
|
||||||
child: Row(
|
color: Colors.white,
|
||||||
mainAxisSize: MainAxisSize.min,
|
),
|
||||||
children: <Widget>[
|
child: Row(
|
||||||
Text(
|
mainAxisSize: MainAxisSize.min,
|
||||||
SCAppLocalizations.of(context)!.scrollToTheBottom,
|
children: <Widget>[
|
||||||
style: TextStyle(fontSize: 10.sp),
|
Text(
|
||||||
),
|
SCAppLocalizations.of(context)!.scrollToTheBottom,
|
||||||
SizedBox(width: 4.w),
|
style: TextStyle(fontSize: 10.sp),
|
||||||
Icon(Icons.chevron_right, size: 10.w),
|
),
|
||||||
],
|
SizedBox(width: 4.w),
|
||||||
),
|
Icon(Icons.chevron_right, size: 10.w),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
}
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import 'package:yumi/shared/business_logic/models/res/join_room_res.dart';
|
|||||||
import 'package:yumi/services/gift/gift_animation_manager.dart';
|
import 'package:yumi/services/gift/gift_animation_manager.dart';
|
||||||
import 'package:yumi/services/gift/gift_system_manager.dart';
|
import 'package:yumi/services/gift/gift_system_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/anim/l_gift_animal_view.dart';
|
import 'package:yumi/ui_kit/widgets/room/anim/l_gift_animal_view.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart';
|
import 'package:yumi/ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart';
|
||||||
@ -40,12 +41,16 @@ class VoiceRoomPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _VoiceRoomPageState extends State<VoiceRoomPage>
|
class _VoiceRoomPageState extends State<VoiceRoomPage>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
|
static const Duration _luckyGiftComboWindow = Duration(seconds: 3);
|
||||||
|
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
final List<Widget> _pages = [AllChatPage(), ChatPage(), GiftChatPage()];
|
final List<Widget> _pages = [AllChatPage(), ChatPage(), GiftChatPage()];
|
||||||
final List<Widget> _tabs = [];
|
final List<Widget> _tabs = [];
|
||||||
late StreamSubscription _subscription;
|
late StreamSubscription _subscription;
|
||||||
final RoomGiftSeatFlightController _giftSeatFlightController =
|
final RoomGiftSeatFlightController _giftSeatFlightController =
|
||||||
RoomGiftSeatFlightController();
|
RoomGiftSeatFlightController();
|
||||||
|
final Map<String, _LuckyGiftComboSession> _luckyGiftComboSessions =
|
||||||
|
<String, _LuckyGiftComboSession>{};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -64,6 +69,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).toggleGiftAnimationVisibility(false);
|
).toggleGiftAnimationVisibility(false);
|
||||||
|
_clearLuckyGiftComboSessions();
|
||||||
_giftSeatFlightController.clear();
|
_giftSeatFlightController.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -75,6 +81,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
if (rtmProvider.msgFloatingGiftListener == _floatingGiftListener) {
|
if (rtmProvider.msgFloatingGiftListener == _floatingGiftListener) {
|
||||||
rtmProvider.msgFloatingGiftListener = null;
|
rtmProvider.msgFloatingGiftListener = null;
|
||||||
}
|
}
|
||||||
|
_clearLuckyGiftComboSessions();
|
||||||
_giftSeatFlightController.clear();
|
_giftSeatFlightController.clear();
|
||||||
_tabController.dispose(); // 释放资源
|
_tabController.dispose(); // 释放资源
|
||||||
_subscription.cancel();
|
_subscription.cancel();
|
||||||
@ -281,6 +288,11 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
|
|
||||||
final giftPhoto = (msg.gift?.giftPhoto ?? "").trim();
|
final giftPhoto = (msg.gift?.giftPhoto ?? "").trim();
|
||||||
final targetUserId = _resolveGiftTargetUserId(msg);
|
final targetUserId = _resolveGiftTargetUserId(msg);
|
||||||
|
final isLuckyGift = _isLuckyGiftMessage(msg);
|
||||||
|
if (isLuckyGift) {
|
||||||
|
_handleLuckyGiftComboVisuals(msg, giftPhoto, targetUserId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_shouldPlaySeatFlightGiftAnimation(msg) && targetUserId != null) {
|
if (_shouldPlaySeatFlightGiftAnimation(msg) && targetUserId != null) {
|
||||||
_giftSeatFlightController.enqueue(
|
_giftSeatFlightController.enqueue(
|
||||||
RoomGiftSeatFlightRequest(
|
RoomGiftSeatFlightRequest(
|
||||||
@ -293,6 +305,82 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isLuckyGiftMessage(Msg msg) {
|
||||||
|
final giftTab = (msg.gift?.giftTab ?? '').trim();
|
||||||
|
return giftTab == "LUCK" || giftTab == SCGiftType.LUCKY_GIFT.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleLuckyGiftComboVisuals(
|
||||||
|
Msg msg,
|
||||||
|
String giftPhoto,
|
||||||
|
String? targetUserId,
|
||||||
|
) {
|
||||||
|
final quantity = (msg.number ?? 0).floor();
|
||||||
|
if (quantity <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final sessionKey = _buildLuckyGiftComboSessionKey(msg, targetUserId);
|
||||||
|
final session = _luckyGiftComboSessions.putIfAbsent(
|
||||||
|
sessionKey,
|
||||||
|
() => _LuckyGiftComboSession(),
|
||||||
|
);
|
||||||
|
session.totalCount += quantity;
|
||||||
|
|
||||||
|
final highestMilestone =
|
||||||
|
SocialChatGiftSystemManager.resolveHighestReachedLuckGiftMilestone(
|
||||||
|
session.totalCount,
|
||||||
|
);
|
||||||
|
if (highestMilestone != null &&
|
||||||
|
highestMilestone > session.highestPlayedMilestone &&
|
||||||
|
SCGlobalConfig.isLuckGiftSpecialEffects) {
|
||||||
|
final effectPath =
|
||||||
|
SocialChatGiftSystemManager.resolveLuckGiftComboEffectPath(
|
||||||
|
highestMilestone,
|
||||||
|
);
|
||||||
|
if (effectPath != null && effectPath.isNotEmpty) {
|
||||||
|
SCGiftVapSvgaManager().play(effectPath, priority: 200);
|
||||||
|
session.highestPlayedMilestone = highestMilestone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_shouldPlaySeatFlightGiftAnimation(msg) &&
|
||||||
|
targetUserId != null &&
|
||||||
|
giftPhoto.isNotEmpty) {
|
||||||
|
session.pendingFlightRequest = RoomGiftSeatFlightRequest(
|
||||||
|
imagePath: giftPhoto,
|
||||||
|
targetUserId: targetUserId,
|
||||||
|
beginSize: 96.w,
|
||||||
|
endSize: 28.w,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
session.flushTimer?.cancel();
|
||||||
|
session.flushTimer = Timer(_luckyGiftComboWindow, () {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final activeSession = _luckyGiftComboSessions.remove(sessionKey);
|
||||||
|
final pendingFlightRequest = activeSession?.pendingFlightRequest;
|
||||||
|
activeSession?.dispose();
|
||||||
|
if (pendingFlightRequest != null) {
|
||||||
|
_giftSeatFlightController.enqueue(pendingFlightRequest);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
String _buildLuckyGiftComboSessionKey(Msg msg, String? targetUserId) {
|
||||||
|
final senderId = (msg.user?.id ?? '').trim();
|
||||||
|
final giftId = (msg.gift?.id ?? '').trim();
|
||||||
|
return '$giftId|$senderId|${targetUserId ?? ""}';
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearLuckyGiftComboSessions() {
|
||||||
|
for (final session in _luckyGiftComboSessions.values) {
|
||||||
|
session.dispose();
|
||||||
|
}
|
||||||
|
_luckyGiftComboSessions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
bool _shouldPlaySeatFlightGiftAnimation(Msg msg) {
|
bool _shouldPlaySeatFlightGiftAnimation(Msg msg) {
|
||||||
final gift = msg.gift;
|
final gift = msg.gift;
|
||||||
if (gift == null) {
|
if (gift == null) {
|
||||||
@ -355,3 +443,14 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _LuckyGiftComboSession {
|
||||||
|
Timer? flushTimer;
|
||||||
|
int totalCount = 0;
|
||||||
|
int highestPlayedMilestone = 0;
|
||||||
|
RoomGiftSeatFlightRequest? pendingFlightRequest;
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
flushTimer?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -69,6 +69,8 @@ typedef OnMessageRecvC2CReadListener = Function(List<String> messageIDList);
|
|||||||
typedef RtmProvider = RealTimeMessagingManager;
|
typedef RtmProvider = RealTimeMessagingManager;
|
||||||
|
|
||||||
class RealTimeMessagingManager extends ChangeNotifier {
|
class RealTimeMessagingManager extends ChangeNotifier {
|
||||||
|
static const int _giftComboMergeWindowMs = 3000;
|
||||||
|
|
||||||
BuildContext? context;
|
BuildContext? context;
|
||||||
|
|
||||||
void _giftFxLog(String message) {
|
void _giftFxLog(String message) {
|
||||||
@ -856,6 +858,17 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
|
|
||||||
/// 添加消息
|
/// 添加消息
|
||||||
addMsg(Msg msg) {
|
addMsg(Msg msg) {
|
||||||
|
final mergedGiftMsg = _mergeGiftMessageIfNeeded(msg);
|
||||||
|
if (mergedGiftMsg != null) {
|
||||||
|
msgAllListener?.call(mergedGiftMsg);
|
||||||
|
msgGiftListener?.call(mergedGiftMsg);
|
||||||
|
if (msg.type == SCRoomMsgType.gift) {
|
||||||
|
msgFloatingGiftListener?.call(msg);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
roomAllMsgList.insert(0, msg);
|
roomAllMsgList.insert(0, msg);
|
||||||
if (roomAllMsgList.length > 250) {
|
if (roomAllMsgList.length > 250) {
|
||||||
print('大于200条消息');
|
print('大于200条消息');
|
||||||
@ -891,6 +904,59 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Msg? _mergeGiftMessageIfNeeded(Msg incoming) {
|
||||||
|
if (incoming.type != SCRoomMsgType.gift &&
|
||||||
|
incoming.type != SCRoomMsgType.luckGiftAnimOther) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final mergeTarget = _findMergeableGiftMessage(incoming);
|
||||||
|
if (mergeTarget == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeTarget.number = (mergeTarget.number ?? 0) + (incoming.number ?? 0);
|
||||||
|
mergeTarget.time = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
if ((incoming.msg ?? "").trim().isNotEmpty) {
|
||||||
|
mergeTarget.msg = incoming.msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveMessageToFront(roomGiftMsgList, mergeTarget);
|
||||||
|
_moveMessageToFront(roomAllMsgList, mergeTarget);
|
||||||
|
return mergeTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg? _findMergeableGiftMessage(Msg incoming) {
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
for (final existing in roomGiftMsgList) {
|
||||||
|
if ((existing.time ?? 0) <= 0 ||
|
||||||
|
now - (existing.time ?? 0) > _giftComboMergeWindowMs) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_isSameGiftComboMessage(existing, incoming)) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isSameGiftComboMessage(Msg existing, Msg incoming) {
|
||||||
|
return existing.type == incoming.type &&
|
||||||
|
existing.groupId == incoming.groupId &&
|
||||||
|
existing.user?.id == incoming.user?.id &&
|
||||||
|
existing.toUser?.id == incoming.toUser?.id &&
|
||||||
|
existing.gift?.id == incoming.gift?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _moveMessageToFront(List<Msg> messages, Msg target) {
|
||||||
|
final index = messages.indexOf(target);
|
||||||
|
if (index <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messages.removeAt(index);
|
||||||
|
messages.insert(0, target);
|
||||||
|
}
|
||||||
|
|
||||||
bool isLogout = false;
|
bool isLogout = false;
|
||||||
|
|
||||||
logout() async {
|
logout() async {
|
||||||
|
|||||||
@ -8,6 +8,65 @@ import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
|||||||
typedef GiftProvider = SocialChatGiftSystemManager;
|
typedef GiftProvider = SocialChatGiftSystemManager;
|
||||||
|
|
||||||
class SocialChatGiftSystemManager extends ChangeNotifier {
|
class SocialChatGiftSystemManager extends ChangeNotifier {
|
||||||
|
static const Map<int, String> _luckGiftComboEffectAssets = {
|
||||||
|
10: "sc_images/room/anim/luck_gift/luck_gift_combo_count_10.svga",
|
||||||
|
30: "sc_images/room/anim/luck_gift/luck_gift_combo_count_30.svga",
|
||||||
|
50: "sc_images/room/anim/luck_gift/luck_gift_combo_count_50.svga",
|
||||||
|
66: "sc_images/room/anim/luck_gift/luck_gift_combo_count_66.svga",
|
||||||
|
100: "sc_images/room/anim/luck_gift/luck_gift_combo_count_100.svga",
|
||||||
|
300: "sc_images/room/anim/luck_gift/luck_gift_combo_count_300.svga",
|
||||||
|
400: "sc_images/room/anim/luck_gift/luck_gift_combo_count_400.svga",
|
||||||
|
666: "sc_images/room/anim/luck_gift/luck_gift_combo_count_666.svga",
|
||||||
|
777: "sc_images/room/anim/luck_gift/luck_gift_combo_count_777.svga",
|
||||||
|
2000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_2000.svga",
|
||||||
|
3000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_3000.svga",
|
||||||
|
5000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_5000.svga",
|
||||||
|
6000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_6000.svga",
|
||||||
|
7000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_7000.svga",
|
||||||
|
8000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_8000.svga",
|
||||||
|
10000: "sc_images/room/anim/luck_gift/luck_gift_combo_count_10000.svga",
|
||||||
|
};
|
||||||
|
static const List<int> _luckGiftMilestones = <int>[
|
||||||
|
10,
|
||||||
|
20,
|
||||||
|
30,
|
||||||
|
50,
|
||||||
|
66,
|
||||||
|
88,
|
||||||
|
100,
|
||||||
|
200,
|
||||||
|
300,
|
||||||
|
400,
|
||||||
|
500,
|
||||||
|
666,
|
||||||
|
777,
|
||||||
|
888,
|
||||||
|
1000,
|
||||||
|
1500,
|
||||||
|
2000,
|
||||||
|
3000,
|
||||||
|
5000,
|
||||||
|
10000,
|
||||||
|
15000,
|
||||||
|
20000,
|
||||||
|
25000,
|
||||||
|
30000,
|
||||||
|
35000,
|
||||||
|
40000,
|
||||||
|
45000,
|
||||||
|
50000,
|
||||||
|
55000,
|
||||||
|
60000,
|
||||||
|
65000,
|
||||||
|
70000,
|
||||||
|
75000,
|
||||||
|
80000,
|
||||||
|
85000,
|
||||||
|
90000,
|
||||||
|
95000,
|
||||||
|
100000,
|
||||||
|
];
|
||||||
|
|
||||||
///幸运礼物就不显示这个特效
|
///幸运礼物就不显示这个特效
|
||||||
bool hideLGiftAnimal = false;
|
bool hideLGiftAnimal = false;
|
||||||
|
|
||||||
@ -129,51 +188,81 @@ class SocialChatGiftSystemManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void modifyLuckyGiftCount(
|
void modifyLuckyGiftCount(
|
||||||
num n,
|
num n,
|
||||||
bool isManyPeople,
|
bool manyPeople,
|
||||||
MicRes first,
|
MicRes first,
|
||||||
SocialChatGiftRes? checkedGift,
|
SocialChatGiftRes? checkedGift,
|
||||||
) {
|
) {
|
||||||
this.number = number + n;
|
number = number + n;
|
||||||
this.isManyPeople = isManyPeople;
|
isManyPeople = manyPeople;
|
||||||
this.toUser = first;
|
toUser = first;
|
||||||
this.gift = checkedGift;
|
gift = checkedGift;
|
||||||
giftAnimSize = 1.4;
|
giftAnimSize = 1.4;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
startGiftAnimation();
|
startGiftAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateLuckyRewardAmount(num n) {
|
void updateLuckyRewardAmount(num n) {
|
||||||
this.awardAmount = n;
|
awardAmount = n;
|
||||||
this.luckGiftObtainCoins = luckGiftObtainCoins + n;
|
luckGiftObtainCoins = luckGiftObtainCoins + n;
|
||||||
this.obtainCoinsAnimSize = 1.4;
|
obtainCoinsAnimSize = 1.4;
|
||||||
this.awardAmountAnimSize = 1.4;
|
awardAmountAnimSize = 1.4;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void startGiftAnimation() {
|
void startGiftAnimation() {
|
||||||
isPlayed.forEach((k, v) {
|
final milestone = resolveHighestReachedLuckGiftMilestone(number);
|
||||||
if (k < number + 1 && !v) {
|
if (milestone == null || (isPlayed[milestone] ?? false)) {
|
||||||
playVisualEffect(k);
|
return;
|
||||||
}
|
}
|
||||||
});
|
playVisualEffect(milestone);
|
||||||
|
_markMilestonesPlayedUpTo(milestone);
|
||||||
}
|
}
|
||||||
|
|
||||||
void playVisualEffect(num n) {
|
void playVisualEffect(num n) {
|
||||||
if (!(isPlayed[n] ?? false)) {
|
if (!(isPlayed[n] ?? false)) {
|
||||||
if (SCGlobalConfig.isLuckGiftSpecialEffects) {
|
if (SCGlobalConfig.isLuckGiftSpecialEffects) {
|
||||||
if (n > 9999) {
|
final comboEffectPath = resolveLuckGiftComboEffectPath(n);
|
||||||
SCGiftVapSvgaManager().play(
|
if (comboEffectPath != null) {
|
||||||
"sc_images/room/anim/luck_gift_count_5000_mor.mp4",
|
SCGiftVapSvgaManager().play(comboEffectPath, priority: 200);
|
||||||
priority: 200,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
SCGiftVapSvgaManager().play(
|
|
||||||
"sc_images/room/anim/luck_gift_count_$n.mp4",
|
|
||||||
priority: 200,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isPlayed[n] = true;
|
isPlayed[n] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _markMilestonesPlayedUpTo(int milestone) {
|
||||||
|
for (final threshold in _luckGiftMilestones) {
|
||||||
|
if (threshold > milestone) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isPlayed[threshold] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int? resolveHighestReachedLuckGiftMilestone(num count) {
|
||||||
|
final normalizedCount = count.floor();
|
||||||
|
int? highest;
|
||||||
|
for (final milestone in _luckGiftMilestones) {
|
||||||
|
if (milestone > normalizedCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
highest = milestone;
|
||||||
|
}
|
||||||
|
return highest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? resolveLuckGiftComboEffectPath(num count) {
|
||||||
|
if (count % 1 != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final normalizedCount = count.toInt();
|
||||||
|
final comboEffectPath = _luckGiftComboEffectAssets[normalizedCount];
|
||||||
|
if (comboEffectPath != null) {
|
||||||
|
return comboEffectPath;
|
||||||
|
}
|
||||||
|
if (normalizedCount > 9999) {
|
||||||
|
return "sc_images/room/anim/luck_gift_count_5000_mor.mp4";
|
||||||
|
}
|
||||||
|
return "sc_images/room/anim/luck_gift_count_$normalizedCount.mp4";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
130
lib/ui_kit/widgets/gift/sc_gift_combo_send_button.dart
Normal file
130
lib/ui_kit/widgets/gift/sc_gift_combo_send_button.dart
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
|
|
||||||
|
class SCGiftComboSendButton extends StatefulWidget {
|
||||||
|
const SCGiftComboSendButton({
|
||||||
|
super.key,
|
||||||
|
required this.label,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.showCountdown,
|
||||||
|
this.countdownAnimation,
|
||||||
|
this.width = 96,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final bool showCountdown;
|
||||||
|
final Animation<double>? countdownAnimation;
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SCGiftComboSendButton> createState() => _SCGiftComboSendButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SCGiftComboSendButtonState extends State<SCGiftComboSendButton> {
|
||||||
|
static const Duration _pressScaleDuration = Duration(milliseconds: 90);
|
||||||
|
static const double _pressedScale = 0.96;
|
||||||
|
|
||||||
|
bool _pressed = false;
|
||||||
|
|
||||||
|
void _setPressed(bool value) {
|
||||||
|
if (_pressed == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_pressed = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final animation =
|
||||||
|
widget.countdownAnimation ?? const AlwaysStoppedAnimation<double>(1);
|
||||||
|
final baseColor = SocialChatTheme.primaryLight;
|
||||||
|
final countdownLightColor = Color.lerp(baseColor, Colors.white, 0.18)!;
|
||||||
|
final countdownDeepColor = Color.lerp(baseColor, Colors.white, 0.04)!;
|
||||||
|
final borderRadius = BorderRadius.circular(5);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTapDown: (_) => _setPressed(true),
|
||||||
|
onTapUp: (_) => _setPressed(false),
|
||||||
|
onTapCancel: () => _setPressed(false),
|
||||||
|
onTap: widget.onPressed,
|
||||||
|
child: AnimatedScale(
|
||||||
|
scale: _pressed ? _pressedScale : 1,
|
||||||
|
duration: _pressScaleDuration,
|
||||||
|
curve: Curves.easeOutCubic,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: animation,
|
||||||
|
builder: (context, child) {
|
||||||
|
final progress =
|
||||||
|
widget.showCountdown
|
||||||
|
? (1 - animation.value).clamp(0.0, 1.0)
|
||||||
|
: 0.0;
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: widget.width,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: baseColor,
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
),
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final countdownWidth = constraints.maxWidth * progress;
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
if (countdownWidth > 0)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: countdownWidth,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.centerRight,
|
||||||
|
end: Alignment.centerLeft,
|
||||||
|
colors: [
|
||||||
|
countdownLightColor,
|
||||||
|
countdownDeepColor,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 8.w,
|
||||||
|
horizontal: 20.w,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.label,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.sp,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,115 +1,139 @@
|
|||||||
import 'dart:async';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
|
||||||
|
class LuckGiftNomorAnimWidget extends StatefulWidget {
|
||||||
class LuckGiftNomorAnimWidget extends StatefulWidget {
|
static const String _rewardFrameAssetPath =
|
||||||
@override
|
"sc_images/room/anim/luck_gift/luck_gift_reward_frame.svga";
|
||||||
_LuckGiftNomorAnimWidgetState createState() =>
|
static const String _rewardFrameFallbackAssetPath =
|
||||||
_LuckGiftNomorAnimWidgetState();
|
"sc_images/room/sc_icon_luck_gift_nomore.webp";
|
||||||
}
|
|
||||||
|
const LuckGiftNomorAnimWidget({super.key});
|
||||||
class _LuckGiftNomorAnimWidgetState extends State<LuckGiftNomorAnimWidget> {
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
State<LuckGiftNomorAnimWidget> createState() =>
|
||||||
super.initState();
|
_LuckGiftNomorAnimWidgetState();
|
||||||
Provider.of<RtmProvider>(context, listen: false).showLuckGiftBigHead = true;
|
}
|
||||||
}
|
|
||||||
|
class _LuckGiftNomorAnimWidgetState extends State<LuckGiftNomorAnimWidget> {
|
||||||
dispose() {
|
@override
|
||||||
super.dispose();
|
void initState() {
|
||||||
}
|
super.initState();
|
||||||
|
Provider.of<RtmProvider>(context, listen: false).showLuckGiftBigHead = true;
|
||||||
@override
|
}
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return IgnorePointer(
|
@override
|
||||||
child: Consumer<RtmProvider>(
|
void dispose() {
|
||||||
builder: (context, provider, child) {
|
super.dispose();
|
||||||
return provider.currentPlayingLuckGift != null
|
}
|
||||||
? Container(
|
|
||||||
height: 380.w,
|
@override
|
||||||
margin: EdgeInsets.only(top: 10.w),
|
Widget build(BuildContext context) {
|
||||||
child: Stack(
|
return IgnorePointer(
|
||||||
children: [
|
child: Consumer<RtmProvider>(
|
||||||
Image.asset(
|
builder: (context, provider, child) {
|
||||||
"sc_images/room/sc_icon_luck_gift_nomore.webp",
|
return provider.currentPlayingLuckGift != null
|
||||||
fit: BoxFit.fitWidth,
|
? Container(
|
||||||
),
|
height: 380.w,
|
||||||
provider.showLuckGiftBigHead?Column(
|
margin: EdgeInsets.only(top: 10.w),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 125.w),
|
SCSvgaAssetWidget(
|
||||||
netImage(
|
key: ValueKey<String>(
|
||||||
url:
|
'${provider.currentPlayingLuckGift?.data?.sendUserId ?? ""}'
|
||||||
provider
|
'|${provider.currentPlayingLuckGift?.data?.acceptUserId ?? ""}'
|
||||||
.currentPlayingLuckGift
|
'|${provider.currentPlayingLuckGift?.data?.awardAmount ?? 0}'
|
||||||
?.data
|
'|${provider.currentPlayingLuckGift?.data?.multiple ?? 0}',
|
||||||
?.userAvatar ??
|
),
|
||||||
"",
|
assetPath: LuckGiftNomorAnimWidget._rewardFrameAssetPath,
|
||||||
shape: BoxShape.circle,
|
width: ScreenUtil().screenWidth,
|
||||||
height: 105.w,
|
height: 380.w,
|
||||||
width: 105.w,
|
fit: BoxFit.fitWidth,
|
||||||
),
|
allowDrawingOverflow: true,
|
||||||
SizedBox(height: 16.w),
|
fallback: Image.asset(
|
||||||
Row(
|
LuckGiftNomorAnimWidget._rewardFrameFallbackAssetPath,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
fit: BoxFit.fitWidth,
|
||||||
children: [
|
),
|
||||||
Image.asset(
|
),
|
||||||
"sc_images/general/sc_icon_jb.png",
|
provider.showLuckGiftBigHead
|
||||||
height: 35.w,
|
? Column(
|
||||||
),
|
mainAxisSize: MainAxisSize.min,
|
||||||
SizedBox(width: 3.w),
|
children: [
|
||||||
Image.asset(
|
SizedBox(height: 125.w),
|
||||||
"sc_images/general/sc_icon_game_numxx.png",
|
netImage(
|
||||||
height: 20.w,
|
url:
|
||||||
),
|
provider
|
||||||
Transform.translate(
|
.currentPlayingLuckGift
|
||||||
offset: Offset(-5, 0),
|
?.data
|
||||||
child: buildNumForGame(
|
?.userAvatar ??
|
||||||
(provider
|
"",
|
||||||
.currentPlayingLuckGift
|
shape: BoxShape.circle,
|
||||||
?.data
|
height: 105.w,
|
||||||
?.awardAmount ??
|
width: 105.w,
|
||||||
0) >
|
),
|
||||||
9999
|
SizedBox(height: 16.w),
|
||||||
? "${((provider.currentPlayingLuckGift?.data?.awardAmount ?? 0) / 1000).toStringAsFixed(0)}k"
|
Row(
|
||||||
: "${(provider.currentPlayingLuckGift?.data?.awardAmount ?? 0)}",
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
size: 22.w,
|
children: [
|
||||||
),
|
Image.asset(
|
||||||
),
|
"sc_images/general/sc_icon_jb.png",
|
||||||
],
|
height: 35.w,
|
||||||
),
|
),
|
||||||
SizedBox(height: 12.w),
|
SizedBox(width: 3.w),
|
||||||
Row(
|
Image.asset(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
"sc_images/general/sc_icon_game_numxx.png",
|
||||||
children: [
|
height: 20.w,
|
||||||
SizedBox(width: 15.w),
|
),
|
||||||
buildNumForGame(
|
Transform.translate(
|
||||||
"${provider.currentPlayingLuckGift?.data?.multiple ?? 0}",
|
offset: Offset(-5, 0),
|
||||||
size: 22.w,
|
child: buildNumForGame(
|
||||||
),
|
(provider
|
||||||
Transform.translate(
|
.currentPlayingLuckGift
|
||||||
offset: Offset(-3, 3),
|
?.data
|
||||||
child: Image.asset(
|
?.awardAmount ??
|
||||||
SCGlobalConfig.lang=="ar"? "sc_images/room/sc_icon_times_text_ar.png":"sc_images/room/sc_icon_times_text_en.png",
|
0) >
|
||||||
height: 12.w,
|
9999
|
||||||
),
|
? "${((provider.currentPlayingLuckGift?.data?.awardAmount ?? 0) / 1000).toStringAsFixed(0)}k"
|
||||||
),
|
: "${(provider.currentPlayingLuckGift?.data?.awardAmount ?? 0)}",
|
||||||
],
|
size: 22.w,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
):Container(),
|
],
|
||||||
],
|
),
|
||||||
),
|
SizedBox(height: 12.w),
|
||||||
)
|
Row(
|
||||||
: Container();
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
},
|
children: [
|
||||||
),
|
SizedBox(width: 15.w),
|
||||||
);
|
buildNumForGame(
|
||||||
}
|
"${provider.currentPlayingLuckGift?.data?.multiple ?? 0}",
|
||||||
}
|
size: 22.w,
|
||||||
|
),
|
||||||
|
Transform.translate(
|
||||||
|
offset: Offset(-3, 3),
|
||||||
|
child: Image.asset(
|
||||||
|
SCGlobalConfig.lang == "ar"
|
||||||
|
? "sc_images/room/sc_icon_times_text_ar.png"
|
||||||
|
: "sc_images/room/sc_icon_times_text_en.png",
|
||||||
|
height: 12.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_10.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_10.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_100.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_100.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_10000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_10000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_2000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_2000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_30.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_30.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_300.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_300.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_3000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_3000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_400.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_400.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_50.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_50.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_5000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_5000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_6000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_6000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_66.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_66.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_666.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_666.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_7000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_7000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_777.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_777.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_8000.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_combo_count_8000.svga
Normal file
Binary file not shown.
BIN
sc_images/room/anim/luck_gift/luck_gift_reward_frame.svga
Normal file
BIN
sc_images/room/anim/luck_gift/luck_gift_reward_frame.svga
Normal file
Binary file not shown.
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from contextlib import contextmanager
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
@ -10,6 +11,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
@ -54,14 +56,16 @@ def ensure_clean_dir(path: Path) -> None:
|
|||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def copy_file(src: Path, dest: Path) -> dict[str, object]:
|
def copy_file(src: Path, dest: Path, *, include_sha256: bool = True) -> dict[str, object]:
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(src, dest)
|
shutil.copy2(src, dest)
|
||||||
return {
|
result: dict[str, object] = {
|
||||||
"path": str(dest.relative_to(ROOT)),
|
"path": str(dest.relative_to(ROOT)),
|
||||||
"sizeBytes": dest.stat().st_size,
|
"sizeBytes": dest.stat().st_size,
|
||||||
"sha256": sha256_of(dest),
|
|
||||||
}
|
}
|
||||||
|
if include_sha256:
|
||||||
|
result["sha256"] = sha256_of(dest)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def copy_tree(src: Path, dest: Path) -> None:
|
def copy_tree(src: Path, dest: Path) -> None:
|
||||||
@ -69,8 +73,54 @@ def copy_tree(src: Path, dest: Path) -> None:
|
|||||||
shutil.copytree(src, dest, dirs_exist_ok=True)
|
shutil.copytree(src, dest, dirs_exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def append_common_flutter_args(command: list[str], args: argparse.Namespace) -> None:
|
def first_existing_path(*candidates: Path) -> Path:
|
||||||
command.extend(["--release", f"--target={args.target}"])
|
for candidate in candidates:
|
||||||
|
if candidate.exists():
|
||||||
|
return candidate
|
||||||
|
raise FileNotFoundError("No build output found in: " + ", ".join(str(candidate) for candidate in candidates))
|
||||||
|
|
||||||
|
|
||||||
|
def timings_bucket(manifest: dict[str, object]) -> dict[str, object]:
|
||||||
|
timings = manifest.get("timings")
|
||||||
|
if not isinstance(timings, dict):
|
||||||
|
timings = {"stages": []}
|
||||||
|
manifest["timings"] = timings
|
||||||
|
|
||||||
|
stages = timings.get("stages")
|
||||||
|
if not isinstance(stages, list):
|
||||||
|
timings["stages"] = []
|
||||||
|
return timings
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def timed_stage(manifest: dict[str, object], key: str, label: str) -> dict[str, object]:
|
||||||
|
stage_started_at = dt.datetime.now()
|
||||||
|
stage_started_perf = time.perf_counter()
|
||||||
|
stage: dict[str, object] = {
|
||||||
|
"key": key,
|
||||||
|
"label": label,
|
||||||
|
"startedAt": stage_started_at.isoformat(timespec="seconds"),
|
||||||
|
}
|
||||||
|
timings_bucket(manifest)["stages"].append(stage)
|
||||||
|
print(f"[stage:start] {label}", flush=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield stage
|
||||||
|
except Exception as exc:
|
||||||
|
stage["status"] = "failed"
|
||||||
|
stage["error"] = str(exc)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
stage["status"] = "succeeded"
|
||||||
|
finally:
|
||||||
|
duration_seconds = round(time.perf_counter() - stage_started_perf, 3)
|
||||||
|
stage["endedAt"] = dt.datetime.now().isoformat(timespec="seconds")
|
||||||
|
stage["durationSeconds"] = duration_seconds
|
||||||
|
print(f"[stage:end] {label} ({duration_seconds:.1f}s)", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def append_common_flutter_args(command: list[str], args: argparse.Namespace, *, build_mode: str = "release") -> None:
|
||||||
|
command.extend([f"--{build_mode}", f"--target={args.target}"])
|
||||||
|
|
||||||
if args.flavor:
|
if args.flavor:
|
||||||
command.extend(["--flavor", args.flavor])
|
command.extend(["--flavor", args.flavor])
|
||||||
@ -110,7 +160,8 @@ def build_android(args: argparse.Namespace, manifest: dict[str, object]) -> None
|
|||||||
appbundle_cmd = ["flutter", "build", "appbundle"]
|
appbundle_cmd = ["flutter", "build", "appbundle"]
|
||||||
append_common_flutter_args(appbundle_cmd, args)
|
append_common_flutter_args(appbundle_cmd, args)
|
||||||
appbundle_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
|
appbundle_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
|
||||||
run_command(appbundle_cmd)
|
with timed_stage(manifest, "android.googlePlayAab", "Android 谷歌发布包(AAB)"):
|
||||||
|
run_command(appbundle_cmd)
|
||||||
|
|
||||||
if profile == "full":
|
if profile == "full":
|
||||||
apk_cmd = [
|
apk_cmd = [
|
||||||
@ -123,19 +174,19 @@ def build_android(args: argparse.Namespace, manifest: dict[str, object]) -> None
|
|||||||
]
|
]
|
||||||
append_common_flutter_args(apk_cmd, args)
|
append_common_flutter_args(apk_cmd, args)
|
||||||
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
|
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
|
||||||
run_command(apk_cmd)
|
with timed_stage(manifest, "android.releaseApks", "Android 多 ABI 正式 APK"):
|
||||||
|
run_command(apk_cmd)
|
||||||
elif profile == "local-arm64":
|
elif profile == "local-arm64":
|
||||||
apk_cmd = [
|
apk_cmd = [
|
||||||
"flutter",
|
"flutter",
|
||||||
"build",
|
"build",
|
||||||
"apk",
|
"apk",
|
||||||
"--split-per-abi",
|
|
||||||
"--target-platform",
|
"--target-platform",
|
||||||
"android-arm64",
|
"android-arm64",
|
||||||
]
|
]
|
||||||
append_common_flutter_args(apk_cmd, args)
|
append_common_flutter_args(apk_cmd, args, build_mode="debug")
|
||||||
apk_cmd.append(f"--split-debug-info={android_symbols_dir.relative_to(ROOT)}")
|
with timed_stage(manifest, "android.localDebugApk", "Android 极速测试包(Debug / ARM64)"):
|
||||||
run_command(apk_cmd)
|
run_command(apk_cmd)
|
||||||
|
|
||||||
google_play_dir = android_output_dir / "google-play"
|
google_play_dir = android_output_dir / "google-play"
|
||||||
local_dir = android_output_dir / "local"
|
local_dir = android_output_dir / "local"
|
||||||
@ -144,28 +195,38 @@ def build_android(args: argparse.Namespace, manifest: dict[str, object]) -> None
|
|||||||
artifact_prefix = f"{args.package_name}-v{args.build_name}-b{args.build_number}"
|
artifact_prefix = f"{args.package_name}-v{args.build_name}-b{args.build_number}"
|
||||||
|
|
||||||
artifacts: dict[str, object] = {}
|
artifacts: dict[str, object] = {}
|
||||||
|
with timed_stage(manifest, "android.collectArtifacts", "整理 Android 产物"):
|
||||||
|
if profile in {"full", "google-play"}:
|
||||||
|
aab_src = ROOT / "build" / "app" / "outputs" / "bundle" / "release" / "app-release.aab"
|
||||||
|
artifacts["googlePlayAab"] = copy_file(aab_src, google_play_dir / f"{artifact_prefix}-google-play.aab")
|
||||||
|
|
||||||
if profile in {"full", "google-play"}:
|
if profile == "full":
|
||||||
aab_src = ROOT / "build" / "app" / "outputs" / "bundle" / "release" / "app-release.aab"
|
arm64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-arm64-v8a-release.apk"
|
||||||
artifacts["googlePlayAab"] = copy_file(aab_src, google_play_dir / f"{artifact_prefix}-google-play.aab")
|
artifacts["localArm64Apk"] = copy_file(arm64_src, local_dir / f"{artifact_prefix}-arm64-v8a.apk")
|
||||||
|
elif profile == "local-arm64":
|
||||||
|
arm64_src = first_existing_path(
|
||||||
|
ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-arm64-v8a-debug.apk",
|
||||||
|
ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-debug.apk",
|
||||||
|
)
|
||||||
|
artifacts["localArm64Apk"] = copy_file(
|
||||||
|
arm64_src,
|
||||||
|
local_dir / f"{artifact_prefix}-arm64-v8a-debug.apk",
|
||||||
|
include_sha256=False,
|
||||||
|
)
|
||||||
|
|
||||||
if profile in {"full", "local-arm64"}:
|
if profile == "full":
|
||||||
arm64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-arm64-v8a-release.apk"
|
armv7_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-armeabi-v7a-release.apk"
|
||||||
artifacts["localArm64Apk"] = copy_file(arm64_src, local_dir / f"{artifact_prefix}-arm64-v8a.apk")
|
x64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-x86_64-release.apk"
|
||||||
|
artifacts["localArmeabiV7aApk"] = copy_file(armv7_src, local_dir / f"{artifact_prefix}-armeabi-v7a.apk")
|
||||||
|
artifacts["testingX64Apk"] = copy_file(x64_src, testing_dir / f"{artifact_prefix}-x86_64-test.apk")
|
||||||
|
|
||||||
if profile == "full":
|
if android_symbols_dir.exists() and profile in {"full", "google-play"}:
|
||||||
armv7_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-armeabi-v7a-release.apk"
|
symbols_parent_dir = google_play_dir if profile in {"full", "google-play"} else local_dir
|
||||||
x64_src = ROOT / "build" / "app" / "outputs" / "flutter-apk" / "app-x86_64-release.apk"
|
copy_tree(android_symbols_dir, symbols_parent_dir / "symbols")
|
||||||
artifacts["localArmeabiV7aApk"] = copy_file(armv7_src, local_dir / f"{artifact_prefix}-armeabi-v7a.apk")
|
artifacts["dartSymbolsDir"] = {
|
||||||
artifacts["testingX64Apk"] = copy_file(x64_src, testing_dir / f"{artifact_prefix}-x86_64-test.apk")
|
"path": str((symbols_parent_dir / "symbols").relative_to(ROOT)),
|
||||||
|
"sizeBytes": sum(path.stat().st_size for path in (symbols_parent_dir / "symbols").rglob("*") if path.is_file()),
|
||||||
if android_symbols_dir.exists():
|
}
|
||||||
symbols_parent_dir = google_play_dir if profile in {"full", "google-play"} else local_dir
|
|
||||||
copy_tree(android_symbols_dir, symbols_parent_dir / "symbols")
|
|
||||||
artifacts["dartSymbolsDir"] = {
|
|
||||||
"path": str((symbols_parent_dir / "symbols").relative_to(ROOT)),
|
|
||||||
"sizeBytes": sum(path.stat().st_size for path in (symbols_parent_dir / "symbols").rglob("*") if path.is_file()),
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest["android"] = artifacts
|
manifest["android"] = artifacts
|
||||||
|
|
||||||
@ -185,7 +246,8 @@ def build_ios(args: argparse.Namespace, manifest: dict[str, object]) -> None:
|
|||||||
else:
|
else:
|
||||||
command.append("--no-codesign")
|
command.append("--no-codesign")
|
||||||
|
|
||||||
run_command(command)
|
with timed_stage(manifest, "ios.buildIpa", "iOS 构建与导出"):
|
||||||
|
run_command(command)
|
||||||
|
|
||||||
artifact_prefix = f"{args.package_name}-v{args.build_name}-b{args.build_number}"
|
artifact_prefix = f"{args.package_name}-v{args.build_name}-b{args.build_number}"
|
||||||
artifacts: dict[str, object] = {}
|
artifacts: dict[str, object] = {}
|
||||||
@ -193,23 +255,24 @@ def build_ios(args: argparse.Namespace, manifest: dict[str, object]) -> None:
|
|||||||
archive_src = ROOT / "build" / "ios" / "archive" / "Runner.xcarchive"
|
archive_src = ROOT / "build" / "ios" / "archive" / "Runner.xcarchive"
|
||||||
ipa_candidates = sorted((ROOT / "build" / "ios" / "ipa").glob("*.ipa"))
|
ipa_candidates = sorted((ROOT / "build" / "ios" / "ipa").glob("*.ipa"))
|
||||||
|
|
||||||
if archive_src.exists():
|
with timed_stage(manifest, "ios.collectArtifacts", "整理 iOS 产物"):
|
||||||
copy_tree(archive_src, ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive")
|
if archive_src.exists():
|
||||||
artifacts["archiveDir"] = {
|
copy_tree(archive_src, ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive")
|
||||||
"path": str((ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive").relative_to(ROOT)),
|
artifacts["archiveDir"] = {
|
||||||
"sizeBytes": sum(path.stat().st_size for path in (ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive").rglob("*") if path.is_file()),
|
"path": str((ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive").relative_to(ROOT)),
|
||||||
}
|
"sizeBytes": sum(path.stat().st_size for path in (ios_output_dir / "archive" / f"{artifact_prefix}.xcarchive").rglob("*") if path.is_file()),
|
||||||
|
}
|
||||||
|
|
||||||
if ipa_candidates:
|
if ipa_candidates:
|
||||||
ipa_src = ipa_candidates[-1]
|
ipa_src = ipa_candidates[-1]
|
||||||
artifacts["ipa"] = copy_file(ipa_src, ios_output_dir / "ipa" / f"{artifact_prefix}.ipa")
|
artifacts["ipa"] = copy_file(ipa_src, ios_output_dir / "ipa" / f"{artifact_prefix}.ipa")
|
||||||
|
|
||||||
if ios_symbols_dir.exists():
|
if ios_symbols_dir.exists():
|
||||||
copy_tree(ios_symbols_dir, ios_output_dir / "symbols")
|
copy_tree(ios_symbols_dir, ios_output_dir / "symbols")
|
||||||
artifacts["dartSymbolsDir"] = {
|
artifacts["dartSymbolsDir"] = {
|
||||||
"path": str((ios_output_dir / "symbols").relative_to(ROOT)),
|
"path": str((ios_output_dir / "symbols").relative_to(ROOT)),
|
||||||
"sizeBytes": sum(path.stat().st_size for path in (ios_output_dir / "symbols").rglob("*") if path.is_file()),
|
"sizeBytes": sum(path.stat().st_size for path in (ios_output_dir / "symbols").rglob("*") if path.is_file()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if not artifacts:
|
if not artifacts:
|
||||||
raise RuntimeError("iOS build finished but no archive or ipa artifact was found.")
|
raise RuntimeError("iOS build finished but no archive or ipa artifact was found.")
|
||||||
@ -276,30 +339,48 @@ def main() -> int:
|
|||||||
parser = create_argument_parser()
|
parser = create_argument_parser()
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.output_dir = args.output_dir.resolve()
|
args.output_dir = args.output_dir.resolve()
|
||||||
|
build_started_at = dt.datetime.now()
|
||||||
|
build_started_perf = time.perf_counter()
|
||||||
|
manifest_path = args.output_dir / "build_manifest.json"
|
||||||
|
|
||||||
args.output_dir.mkdir(parents=True, exist_ok=True)
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
manifest: dict[str, object] = {
|
manifest: dict[str, object] = {
|
||||||
"generatedAt": dt.datetime.now().isoformat(timespec="seconds"),
|
"generatedAt": dt.datetime.now().isoformat(timespec="seconds"),
|
||||||
"packageName": args.package_name,
|
"packageName": args.package_name,
|
||||||
|
"platform": args.platform,
|
||||||
"buildName": args.build_name,
|
"buildName": args.build_name,
|
||||||
"buildNumber": args.build_number,
|
"buildNumber": args.build_number,
|
||||||
"target": args.target,
|
"target": args.target,
|
||||||
"flavor": args.flavor,
|
"flavor": args.flavor,
|
||||||
"outputDir": str(args.output_dir.relative_to(ROOT)),
|
"outputDir": str(args.output_dir.relative_to(ROOT)),
|
||||||
|
"timings": {
|
||||||
|
"startedAt": build_started_at.isoformat(timespec="seconds"),
|
||||||
|
"stages": [],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_build_android(args.platform):
|
exit_code = 0
|
||||||
build_android(args, manifest)
|
try:
|
||||||
if should_build_ios(args.platform):
|
if should_build_android(args.platform):
|
||||||
build_ios(args, manifest)
|
build_android(args, manifest)
|
||||||
|
if should_build_ios(args.platform):
|
||||||
manifest_path = args.output_dir / "build_manifest.json"
|
build_ios(args, manifest)
|
||||||
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
manifest["status"] = "succeeded"
|
||||||
|
print(f"Artifacts copied to: {args.output_dir}")
|
||||||
print(f"Artifacts copied to: {args.output_dir}")
|
except Exception as exc:
|
||||||
print(f"Manifest written to: {manifest_path}")
|
manifest["status"] = "failed"
|
||||||
return 0
|
manifest["error"] = str(exc)
|
||||||
|
exit_code = 1
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
timings = timings_bucket(manifest)
|
||||||
|
timings["endedAt"] = dt.datetime.now().isoformat(timespec="seconds")
|
||||||
|
timings["totalSeconds"] = round(time.perf_counter() - build_started_perf, 3)
|
||||||
|
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||||
|
print(f"Total build time: {timings['totalSeconds']:.1f}s", flush=True)
|
||||||
|
print(f"Manifest written to: {manifest_path}", flush=True)
|
||||||
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
126
test/l10n_ui_smoke_test.dart
Normal file
126
test/l10n_ui_smoke_test.dart
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:yumi/app/config/app_config.dart';
|
||||||
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
|
import 'package:yumi/app_localizations.dart';
|
||||||
|
import 'package:yumi/modules/auth/account/sc_login_with_account_page.dart';
|
||||||
|
import 'package:yumi/modules/settings/language/language_page.dart';
|
||||||
|
import 'package:yumi/services/localization/localization_manager.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
AppConfig.initialize();
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
DataPersistence.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
const locales = <Locale>[
|
||||||
|
Locale('en'),
|
||||||
|
Locale('ar'),
|
||||||
|
Locale('tr'),
|
||||||
|
Locale('bn'),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (final locale in locales) {
|
||||||
|
group('Locale ${locale.languageCode}', () {
|
||||||
|
testWidgets('LanguagePage renders cleanly', (tester) async {
|
||||||
|
await _pumpLocalizedPage(
|
||||||
|
tester: tester,
|
||||||
|
locale: locale,
|
||||||
|
child: LanguagePage(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final exceptions = _drainExceptions(tester);
|
||||||
|
expect(exceptions, isEmpty, reason: exceptions.join('\n\n'));
|
||||||
|
expect(
|
||||||
|
tester
|
||||||
|
.widget<MaterialApp>(find.byType(MaterialApp))
|
||||||
|
.locale
|
||||||
|
?.languageCode,
|
||||||
|
locale.languageCode,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('SCLoginWithAccountPage renders cleanly', (tester) async {
|
||||||
|
await _pumpLocalizedPage(
|
||||||
|
tester: tester,
|
||||||
|
locale: locale,
|
||||||
|
child: const SCLoginWithAccountPage(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final exceptions = _drainExceptions(tester);
|
||||||
|
expect(exceptions, isEmpty, reason: exceptions.join('\n\n'));
|
||||||
|
expect(
|
||||||
|
tester
|
||||||
|
.widget<MaterialApp>(find.byType(MaterialApp))
|
||||||
|
.locale
|
||||||
|
?.languageCode,
|
||||||
|
locale.languageCode,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _pumpLocalizedPage({
|
||||||
|
required WidgetTester tester,
|
||||||
|
required Locale locale,
|
||||||
|
required Widget child,
|
||||||
|
}) async {
|
||||||
|
SharedPreferences.setMockInitialValues({'lang': locale.languageCode});
|
||||||
|
await DataPersistence.initialize();
|
||||||
|
await tester.binding.setSurfaceSize(const Size(390, 844));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
ChangeNotifierProvider(
|
||||||
|
create: (_) => LocalizationManager(),
|
||||||
|
child: ScreenUtilInit(
|
||||||
|
designSize: Size(SCScreen.designWidth, SCScreen.designHeight),
|
||||||
|
splitScreenMode: false,
|
||||||
|
minTextAdapt: true,
|
||||||
|
builder: (context, _) {
|
||||||
|
return MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
locale: locale,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
SCAppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: const [
|
||||||
|
Locale('en'),
|
||||||
|
Locale('ar'),
|
||||||
|
Locale('tr'),
|
||||||
|
Locale('bn'),
|
||||||
|
],
|
||||||
|
home: RepaintBoundary(
|
||||||
|
key: const ValueKey('capture_boundary'),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
await tester.pump(const Duration(milliseconds: 200));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object> _drainExceptions(WidgetTester tester) {
|
||||||
|
final exceptions = <Object>[];
|
||||||
|
Object? exception;
|
||||||
|
while ((exception = tester.takeException()) != null) {
|
||||||
|
exceptions.add(exception!);
|
||||||
|
}
|
||||||
|
return exceptions;
|
||||||
|
}
|
||||||
5
需求进度.md
5
需求进度.md
@ -13,6 +13,11 @@
|
|||||||
- 本轮按需求暂未处理网络链路上的启动等待,例如审核态检查或远端启动页配置请求。
|
- 本轮按需求暂未处理网络链路上的启动等待,例如审核态检查或远端启动页配置请求。
|
||||||
|
|
||||||
## 已完成模块
|
## 已完成模块
|
||||||
|
- 已继续优化幸运/CP 礼物连击体验:房间 `Gift/All` 消息面板现在会对短时间内同一发送者、同一目标、同一礼物的连续赠送做聚合更新,不再每点一次就追加一条新消息,而是复用同一条礼物播报并持续刷新成 `xN`;同时礼物页底部发送按钮已补上本地连击反馈,Lucky/CP/Magic 类礼物连续点击时会显示一条从右向左收缩的浅色倒计时渐变条,并同步累加当前连击数量,用户能更直观看到连击窗口是否还在持续。
|
||||||
|
- 已继续给发送端补齐连击请求聚合:礼物页当前会对 Lucky/CP/Magic 这类连击型礼物启用约 `200ms` 的本地批量窗口,用户在短时间内连续点击 `Send` 时,前端会先把同一礼物、同一目标集合、同一房间的点击数量累加到同一批次里,再统一发一次接口和一次房间 RTM 消息;这样高连击时不再按点击次数直冲 `/gift/batch` 或 `/gift/give/lucky-gift`,同时也避免本地回显和房间消息量被线性放大。
|
||||||
|
- 已继续收敛幸运礼物高连击时的播放策略:房间内幸运礼物现在按 3 秒连击会话聚合,连击期间只更新房内上飘与总次数,不再每次都立刻飞向麦位;同一轮连击结束后才会向目标麦位补播一次飞行动画,避免 `1000` 连击把静态礼物飞行动画排成超长队列;同时幸运礼物的档位特效已改为只命中当前会话累计数量对应的最高有效档位,不再把中间跨过的 `10/20/30/...` 全部补播一遍。
|
||||||
|
- 已定位到幸运礼物“中奖通知”使用的是房间页顶层 `LuckGiftNomorAnimWidget`,此前背景一直是静态 `sc_icon_luck_gift_nomore.webp`;当前已改为优先播放本地 `sc_images/room/anim/luck_gift/luck_gift_reward_frame.svga`,并保留原 `webp` 作为失败兜底,这样幸运礼物中奖弹层会直接复用新的奖励边框动效资源。
|
||||||
|
- 已开始接入幸运礼物连击档位的新动效资源:桌面“幸运礼物相关”目录下的 SVGA 已统一导入到 `sc_images/room/anim/luck_gift/`,并按规范重命名为 `luck_gift_combo_count_10.svga / luck_gift_combo_count_666.svga / luck_gift_combo_count_10000.svga` 这一类统一英文命名;当前连击阈值触发仍复用 `GiftSystemManager.playVisualEffect()`,命中已提供素材的档位时优先播放新的本地 SVGA,未提供素材的档位继续保留旧兜底逻辑,避免影响现有幸运礼物连击链路。
|
||||||
- 已将语言房送礼链路接入新的“中心停留后飞向目标麦位”组件,但只对无自带特效的静态 PNG 礼物生效:当前带自身 `SVGA/MP4/VAP` 动画或被识别为全屏礼物特效的礼物保持原有播放逻辑不变;只有普通 PNG 礼物会额外触发“屏幕中央停留 -> 三连残影飞向被赠送麦位”的补充动效,避免和自带礼物特效重复叠播。
|
- 已将语言房送礼链路接入新的“中心停留后飞向目标麦位”组件,但只对无自带特效的静态 PNG 礼物生效:当前带自身 `SVGA/MP4/VAP` 动画或被识别为全屏礼物特效的礼物保持原有播放逻辑不变;只有普通 PNG 礼物会额外触发“屏幕中央停留 -> 三连残影飞向被赠送麦位”的补充动效,避免和自带礼物特效重复叠播。
|
||||||
- 已继续收敛语言房送礼飞行动效的命中条件:上一版对普通礼物的过滤过严,既依赖 `giftPhoto` 必须显式以 `.png` 结尾,也会被部分礼物的 `special` 标记误伤,导致不少实际没有自带动画的礼物被提前跳过;当前已改为只排除真实带 `SVGA/MP4/VAP` 动画源的礼物,普通静态封面礼物即使是带 query 的图片 URL、或不是严格 `.png` 后缀,也会正常触发“中心停留 -> 飞向目标麦位”的补充动画。
|
- 已继续收敛语言房送礼飞行动效的命中条件:上一版对普通礼物的过滤过严,既依赖 `giftPhoto` 必须显式以 `.png` 结尾,也会被部分礼物的 `special` 标记误伤,导致不少实际没有自带动画的礼物被提前跳过;当前已改为只排除真实带 `SVGA/MP4/VAP` 动画源的礼物,普通静态封面礼物即使是带 query 的图片 URL、或不是严格 `.png` 后缀,也会正常触发“中心停留 -> 飞向目标麦位”的补充动画。
|
||||||
- 已将语言房送礼飞行动画从房间页内部 `Stack` 提升到应用根层,挂载方式对齐现有 `SVGA/VAP` 礼物特效层:当前该动画会和 `VapPlusSvgaPlayer` 一样在 `main.dart` 的顶层 builder 中全屏绘制,因此不会再被房间内部聊天区、局部动效或页面层级压住,视觉上更靠前、更容易被用户看到。
|
- 已将语言房送礼飞行动画从房间页内部 `Stack` 提升到应用根层,挂载方式对齐现有 `SVGA/VAP` 礼物特效层:当前该动画会和 `VapPlusSvgaPlayer` 一样在 `main.dart` 的顶层 builder 中全屏绘制,因此不会再被房间内部聊天区、局部动效或页面层级压住,视觉上更靠前、更容易被用户看到。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user