Compare commits
No commits in common. "main" and "test" have entirely different histories.
@ -244,7 +244,6 @@
|
|||||||
"reject": "رفض",
|
"reject": "رفض",
|
||||||
"sendUser": "إرسال المستخدم",
|
"sendUser": "إرسال المستخدم",
|
||||||
"bio": "معلومات شخصية",
|
"bio": "معلومات شخصية",
|
||||||
"introduction": "مقدمة",
|
|
||||||
"delete": "حذف",
|
"delete": "حذف",
|
||||||
"sendCoupontips": "هل أنت متأكد أنك تريد إرسال هذه القسيمة لهذا المستخدم؟",
|
"sendCoupontips": "هل أنت متأكد أنك تريد إرسال هذه القسيمة لهذا المستخدم؟",
|
||||||
"recallThisMessage": "هل تريد استرجاع هذه الرسالة؟",
|
"recallThisMessage": "هل تريد استرجاع هذه الرسالة؟",
|
||||||
@ -258,21 +257,6 @@
|
|||||||
"inviteYouToBecomeBD": "ندعوك للانضمام كـ BD.",
|
"inviteYouToBecomeBD": "ندعوك للانضمام كـ BD.",
|
||||||
"confirmAcceptTheInvitation": "هل تريد تأكيد قبول الدعوة؟",
|
"confirmAcceptTheInvitation": "هل تريد تأكيد قبول الدعوة؟",
|
||||||
"confirmDeclineTheInvitation": "تأكيد رفض الدعوة؟",
|
"confirmDeclineTheInvitation": "تأكيد رفض الدعوة؟",
|
||||||
"cpInviteRejectedByOther": "رفض الطرف الآخر أن يصبح {1}.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"friends": "أصدقاء",
|
"friends": "أصدقاء",
|
||||||
"language": "اللغة",
|
"language": "اللغة",
|
||||||
"feedback": "ملاحظات",
|
"feedback": "ملاحظات",
|
||||||
@ -571,7 +555,6 @@
|
|||||||
"unFollow": "إلغاء المتابعة",
|
"unFollow": "إلغاء المتابعة",
|
||||||
"searchInputHint": "أدخل رقم الحساب / الغرفة",
|
"searchInputHint": "أدخل رقم الحساب / الغرفة",
|
||||||
"kickRoomTips": "لقد تم طردك من الغرفة",
|
"kickRoomTips": "لقد تم طردك من الغرفة",
|
||||||
"roomKickedEntryBlockedTips": "لقد أخرجك أحد المشرفين من الغرفة، ولا يمكنك الدخول مؤقتًا.",
|
|
||||||
"joinRoomTips": "انضم إلى الغرفة!",
|
"joinRoomTips": "انضم إلى الغرفة!",
|
||||||
"roomSetting": "إعدادات الغرفة",
|
"roomSetting": "إعدادات الغرفة",
|
||||||
"roomDetails": "تفاصيل الغرفة",
|
"roomDetails": "تفاصيل الغرفة",
|
||||||
|
|||||||
@ -247,7 +247,6 @@
|
|||||||
"delete": "মুছে ফেলুন",
|
"delete": "মুছে ফেলুন",
|
||||||
"copy": "কপি করুন",
|
"copy": "কপি করুন",
|
||||||
"bio": "জীবনী",
|
"bio": "জীবনী",
|
||||||
"introduction": "পরিচিতি",
|
|
||||||
"useCoupontips": "আপনি কি কুপন ব্যবহার করতে চান?",
|
"useCoupontips": "আপনি কি কুপন ব্যবহার করতে চান?",
|
||||||
"searchUserId": "ব্যবহারকারী ID সার্চ করুন",
|
"searchUserId": "ব্যবহারকারী ID সার্চ করুন",
|
||||||
"sendUser": "ব্যবহারকারীকে পাঠান",
|
"sendUser": "ব্যবহারকারীকে পাঠান",
|
||||||
@ -325,21 +324,6 @@
|
|||||||
"adminInviteRechargeAgent": "আপনাকে রিচার্জ এজেন্ট হতে আমন্ত্রণ জানাচ্ছি।",
|
"adminInviteRechargeAgent": "আপনাকে রিচার্জ এজেন্ট হতে আমন্ত্রণ জানাচ্ছি।",
|
||||||
"confirmAcceptTheInvitation": "আপনি কি আমন্ত্রণ গ্রহণ করতে নিশ্চিত করছেন?",
|
"confirmAcceptTheInvitation": "আপনি কি আমন্ত্রণ গ্রহণ করতে নিশ্চিত করছেন?",
|
||||||
"confirmDeclineTheInvitation": "আপনি কি আমন্ত্রণ প্রত্যাখ্যান করতে নিশ্চিত করছেন?",
|
"confirmDeclineTheInvitation": "আপনি কি আমন্ত্রণ প্রত্যাখ্যান করতে নিশ্চিত করছেন?",
|
||||||
"cpInviteRejectedByOther": "অন্য পক্ষ {1} হতে অস্বীকার করেছে।",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "হোস্ট",
|
"host": "হোস্ট",
|
||||||
"following": "ফলো করা হচ্ছে",
|
"following": "ফলো করা হচ্ছে",
|
||||||
"agent": "এজেন্ট",
|
"agent": "এজেন্ট",
|
||||||
@ -501,7 +485,6 @@
|
|||||||
"customized": "কাস্টমাইজড",
|
"customized": "কাস্টমাইজড",
|
||||||
"searchInputHint": "অ্যাকাউন্ট/রুম নম্বর লিখুন",
|
"searchInputHint": "অ্যাকাউন্ট/রুম নম্বর লিখুন",
|
||||||
"kickRoomTips": "আপনাকে রুম থেকে বের করা হয়েছে।",
|
"kickRoomTips": "আপনাকে রুম থেকে বের করা হয়েছে।",
|
||||||
"roomKickedEntryBlockedTips": "একজন অ্যাডমিন আপনাকে রুম থেকে সরিয়ে দিয়েছেন, আপাতত আপনি প্রবেশ করতে পারবেন না।",
|
|
||||||
"joinRoomTips": "রুমে যোগদান করেছেন !",
|
"joinRoomTips": "রুমে যোগদান করেছেন !",
|
||||||
"roomSetting": "রুম সেটিংস",
|
"roomSetting": "রুম সেটিংস",
|
||||||
"roomDetails": "রুম বিবরণ",
|
"roomDetails": "রুম বিবরণ",
|
||||||
|
|||||||
@ -217,7 +217,6 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"bio": "Bio",
|
"bio": "Bio",
|
||||||
"introduction": "Introduction",
|
|
||||||
"useCoupontips": "Are you sure you want to use the coupon?",
|
"useCoupontips": "Are you sure you want to use the coupon?",
|
||||||
"searchUserId": "Search user's ID",
|
"searchUserId": "Search user's ID",
|
||||||
"sendUser": "Send User",
|
"sendUser": "Send User",
|
||||||
@ -295,21 +294,6 @@
|
|||||||
"adminInviteRechargeAgent": "Invite you to become a Recharge agent.",
|
"adminInviteRechargeAgent": "Invite you to become a Recharge agent.",
|
||||||
"confirmAcceptTheInvitation": "Confirm to accept the invitation?",
|
"confirmAcceptTheInvitation": "Confirm to accept the invitation?",
|
||||||
"confirmDeclineTheInvitation": "Confirm declining the invitation?",
|
"confirmDeclineTheInvitation": "Confirm declining the invitation?",
|
||||||
"cpInviteRejectedByOther": "The other party declined to become {1}.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"following": "Following",
|
"following": "Following",
|
||||||
"agent": "Agency",
|
"agent": "Agency",
|
||||||
@ -471,7 +455,6 @@
|
|||||||
"customized": "Customized",
|
"customized": "Customized",
|
||||||
"searchInputHint": "Enter account/room number",
|
"searchInputHint": "Enter account/room number",
|
||||||
"kickRoomTips": "You have been kicked out of the room.",
|
"kickRoomTips": "You have been kicked out of the room.",
|
||||||
"roomKickedEntryBlockedTips": "You have been removed from the room by an admin and cannot enter for now.",
|
|
||||||
"joinRoomTips": "Joined the room!",
|
"joinRoomTips": "Joined the room!",
|
||||||
"roomSetting": "Room Setting",
|
"roomSetting": "Room Setting",
|
||||||
"roomDetails": "Room Details",
|
"roomDetails": "Room Details",
|
||||||
@ -575,7 +558,7 @@
|
|||||||
"openTheMic": "Open the mic",
|
"openTheMic": "Open the mic",
|
||||||
"muteTheMic": "Mute the mic",
|
"muteTheMic": "Mute the mic",
|
||||||
"unlockTheMic": "Unlock the mic",
|
"unlockTheMic": "Unlock the mic",
|
||||||
"leavelTheMic": "Leave mic",
|
"leavelTheMic": "Leave the mic",
|
||||||
"lockTheMic": "Lock the mic",
|
"lockTheMic": "Lock the mic",
|
||||||
"removeTheMic": "Remove the mic",
|
"removeTheMic": "Remove the mic",
|
||||||
"inviteToTheMicrophone": "Invite to the microphone",
|
"inviteToTheMicrophone": "Invite to the microphone",
|
||||||
|
|||||||
@ -217,7 +217,6 @@
|
|||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"bio": "Biographie",
|
"bio": "Biographie",
|
||||||
"introduction": "Présentation",
|
|
||||||
"useCoupontips": "Êtes-vous sûr de vouloir utiliser le coupon ?",
|
"useCoupontips": "Êtes-vous sûr de vouloir utiliser le coupon ?",
|
||||||
"searchUserId": "Rechercher l'ID de l'utilisateur",
|
"searchUserId": "Rechercher l'ID de l'utilisateur",
|
||||||
"sendUser": "Envoyer l'utilisateur",
|
"sendUser": "Envoyer l'utilisateur",
|
||||||
@ -295,21 +294,6 @@
|
|||||||
"adminInviteRechargeAgent": "Vous invite à devenir agent Recharge.",
|
"adminInviteRechargeAgent": "Vous invite à devenir agent Recharge.",
|
||||||
"confirmAcceptTheInvitation": "Confirmer pour accepter l'invitation ?",
|
"confirmAcceptTheInvitation": "Confirmer pour accepter l'invitation ?",
|
||||||
"confirmDeclineTheInvitation": "Confirmer le refus de l'invitation ?",
|
"confirmDeclineTheInvitation": "Confirmer le refus de l'invitation ?",
|
||||||
"cpInviteRejectedByOther": "L'autre personne a refusé de devenir {1}.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "Hôte",
|
"host": "Hôte",
|
||||||
"following": "Suite",
|
"following": "Suite",
|
||||||
"agent": "Agence",
|
"agent": "Agence",
|
||||||
@ -471,7 +455,6 @@
|
|||||||
"customized": "Personnalisé",
|
"customized": "Personnalisé",
|
||||||
"searchInputHint": "Entrez le numéro de compte/de chambre",
|
"searchInputHint": "Entrez le numéro de compte/de chambre",
|
||||||
"kickRoomTips": "Vous avez été expulsé de la pièce.",
|
"kickRoomTips": "Vous avez été expulsé de la pièce.",
|
||||||
"roomKickedEntryBlockedTips": "Un administrateur vous a retiré de la salle, vous ne pouvez pas y entrer pour le moment.",
|
|
||||||
"joinRoomTips": "J'ai rejoint la salle !",
|
"joinRoomTips": "J'ai rejoint la salle !",
|
||||||
"roomSetting": "Cadre de la pièce",
|
"roomSetting": "Cadre de la pièce",
|
||||||
"roomDetails": "Détails de la chambre",
|
"roomDetails": "Détails de la chambre",
|
||||||
|
|||||||
@ -217,7 +217,6 @@
|
|||||||
"delete": "Elimina",
|
"delete": "Elimina",
|
||||||
"copy": "Copia",
|
"copy": "Copia",
|
||||||
"bio": "Bio",
|
"bio": "Bio",
|
||||||
"introduction": "Introduzione",
|
|
||||||
"useCoupontips": "Sei sicuro di voler utilizzare il coupon?",
|
"useCoupontips": "Sei sicuro di voler utilizzare il coupon?",
|
||||||
"searchUserId": "Cerca l'ID dell'utente",
|
"searchUserId": "Cerca l'ID dell'utente",
|
||||||
"sendUser": "Invia utente",
|
"sendUser": "Invia utente",
|
||||||
@ -295,21 +294,6 @@
|
|||||||
"adminInviteRechargeAgent": "Invitati a diventare un agente Recharge.",
|
"adminInviteRechargeAgent": "Invitati a diventare un agente Recharge.",
|
||||||
"confirmAcceptTheInvitation": "Confermare per accettare l'invito?",
|
"confirmAcceptTheInvitation": "Confermare per accettare l'invito?",
|
||||||
"confirmDeclineTheInvitation": "Confermi di rifiutare l'invito?",
|
"confirmDeclineTheInvitation": "Confermi di rifiutare l'invito?",
|
||||||
"cpInviteRejectedByOther": "L'altra persona ha rifiutato di diventare {1}.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "Ospite",
|
"host": "Ospite",
|
||||||
"following": "Segue",
|
"following": "Segue",
|
||||||
"agent": "Agenzia",
|
"agent": "Agenzia",
|
||||||
@ -471,7 +455,6 @@
|
|||||||
"customized": "Personalizzato",
|
"customized": "Personalizzato",
|
||||||
"searchInputHint": "Inserisci il numero di conto/stanza",
|
"searchInputHint": "Inserisci il numero di conto/stanza",
|
||||||
"kickRoomTips": "Sei stato cacciato dalla stanza.",
|
"kickRoomTips": "Sei stato cacciato dalla stanza.",
|
||||||
"roomKickedEntryBlockedTips": "Sei stato rimosso dalla stanza da un amministratore e al momento non puoi entrare.",
|
|
||||||
"joinRoomTips": "Entra nella stanza!",
|
"joinRoomTips": "Entra nella stanza!",
|
||||||
"roomSetting": "Impostazione della stanza",
|
"roomSetting": "Impostazione della stanza",
|
||||||
"roomDetails": "Dettagli della camera",
|
"roomDetails": "Dettagli della camera",
|
||||||
|
|||||||
@ -217,7 +217,6 @@
|
|||||||
"delete": "Excluir",
|
"delete": "Excluir",
|
||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"bio": "Biografia",
|
"bio": "Biografia",
|
||||||
"introduction": "Introdução",
|
|
||||||
"useCoupontips": "Tem certeza que deseja usar o cupom?",
|
"useCoupontips": "Tem certeza que deseja usar o cupom?",
|
||||||
"searchUserId": "Pesquisar ID do usuário",
|
"searchUserId": "Pesquisar ID do usuário",
|
||||||
"sendUser": "Enviar usuário",
|
"sendUser": "Enviar usuário",
|
||||||
@ -295,21 +294,6 @@
|
|||||||
"adminInviteRechargeAgent": "Convido você a se tornar um agente Recharge.",
|
"adminInviteRechargeAgent": "Convido você a se tornar um agente Recharge.",
|
||||||
"confirmAcceptTheInvitation": "Confirmar para aceitar o convite?",
|
"confirmAcceptTheInvitation": "Confirmar para aceitar o convite?",
|
||||||
"confirmDeclineTheInvitation": "Confirmar a recusa do convite?",
|
"confirmDeclineTheInvitation": "Confirmar a recusa do convite?",
|
||||||
"cpInviteRejectedByOther": "A outra pessoa recusou se tornar {1}.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "Anfitrião",
|
"host": "Anfitrião",
|
||||||
"following": "Seguindo",
|
"following": "Seguindo",
|
||||||
"agent": "Agência",
|
"agent": "Agência",
|
||||||
@ -471,7 +455,6 @@
|
|||||||
"customized": "Personalizado",
|
"customized": "Personalizado",
|
||||||
"searchInputHint": "Insira o número da conta/quarto",
|
"searchInputHint": "Insira o número da conta/quarto",
|
||||||
"kickRoomTips": "Você foi expulso da sala.",
|
"kickRoomTips": "Você foi expulso da sala.",
|
||||||
"roomKickedEntryBlockedTips": "Você foi removido da sala por um administrador e não pode entrar por enquanto.",
|
|
||||||
"joinRoomTips": "Entrei na sala!",
|
"joinRoomTips": "Entrei na sala!",
|
||||||
"roomSetting": "Configuração da sala",
|
"roomSetting": "Configuração da sala",
|
||||||
"roomDetails": "Detalhes do quarto",
|
"roomDetails": "Detalhes do quarto",
|
||||||
|
|||||||
@ -206,7 +206,6 @@
|
|||||||
"delete": "Sil",
|
"delete": "Sil",
|
||||||
"copy": "Kopyala",
|
"copy": "Kopyala",
|
||||||
"bio": "Biyografi",
|
"bio": "Biyografi",
|
||||||
"introduction": "Tanıtım",
|
|
||||||
"useCoupontips": "Kuponu kullanmak istediğinizden emin misiniz?",
|
"useCoupontips": "Kuponu kullanmak istediğinizden emin misiniz?",
|
||||||
"searchUserId": "Kullanıcı ID'sini Ara",
|
"searchUserId": "Kullanıcı ID'sini Ara",
|
||||||
"sendUser": "Kullanıcıya Gönder",
|
"sendUser": "Kullanıcıya Gönder",
|
||||||
@ -294,21 +293,6 @@
|
|||||||
"adminInviteRechargeAgent": "Sizi Yükleme temsilcisi yapmaya davet ediyoruz.",
|
"adminInviteRechargeAgent": "Sizi Yükleme temsilcisi yapmaya davet ediyoruz.",
|
||||||
"confirmAcceptTheInvitation": "Daveti kabul etmek için onaylıyor musunuz?",
|
"confirmAcceptTheInvitation": "Daveti kabul etmek için onaylıyor musunuz?",
|
||||||
"confirmDeclineTheInvitation": "Daveti reddetmek için onaylıyor musunuz?",
|
"confirmDeclineTheInvitation": "Daveti reddetmek için onaylıyor musunuz?",
|
||||||
"cpInviteRejectedByOther": "Karşı taraf {1} olmayı reddetti.",
|
|
||||||
"closeFriendInvitationTitle": "Invitation from a Close Friend",
|
|
||||||
"closeFriendInvitationSubtitle": "Click to view the invitation",
|
|
||||||
"closeFriendInvitationExpired": "The invitation to add a close friend has expired. This action cannot be performed.",
|
|
||||||
"closeFriendRequestDeclined": "Friend request declined",
|
|
||||||
"closeFriendsHaveBecome": "Have become close friends",
|
|
||||||
"otherAcceptedCloseFriendInvite": "The other party accepted your close friend invitation.",
|
|
||||||
"otherDeclinedCloseFriendInvite": "The other party declined your close friend invitation.",
|
|
||||||
"youAcceptedCloseFriendInvite": "You accepted the other party's close friend invitation.",
|
|
||||||
"youDeclinedCloseFriendInvite": "You declined the other party's close friend invitation.",
|
|
||||||
"closeFriendInviteTimedOut": "Close friend invitation timed out.",
|
|
||||||
"cpDismissConfirmMessage": "Removing this relationship will cost {1} coins. Confirm removal?",
|
|
||||||
"cpRelationCp": "CP",
|
|
||||||
"cpRelationBrother": "Brother",
|
|
||||||
"cpRelationSister": "Sister",
|
|
||||||
"host": "Sunucu",
|
"host": "Sunucu",
|
||||||
"following": "Takip Edilenler",
|
"following": "Takip Edilenler",
|
||||||
"agent": "Temsilcilik",
|
"agent": "Temsilcilik",
|
||||||
@ -470,7 +454,6 @@
|
|||||||
"customized": "Özel",
|
"customized": "Özel",
|
||||||
"searchInputHint": "Hesap/oda numarasını girin",
|
"searchInputHint": "Hesap/oda numarasını girin",
|
||||||
"kickRoomTips": "Odadan atıldınız.",
|
"kickRoomTips": "Odadan atıldınız.",
|
||||||
"roomKickedEntryBlockedTips": "Bir yönetici tarafından odadan çıkarıldınız, şu anda giremezsiniz.",
|
|
||||||
"joinRoomTips": "odaya katıldı !",
|
"joinRoomTips": "odaya katıldı !",
|
||||||
"roomSetting": "Oda Ayarları",
|
"roomSetting": "Oda Ayarları",
|
||||||
"roomDetails": "Oda Detayları",
|
"roomDetails": "Oda Detayları",
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>destination</key>
|
|
||||||
<string>export</string>
|
|
||||||
<key>distributionBundleIdentifier</key>
|
|
||||||
<string>com.org.yumiparty</string>
|
|
||||||
<key>manageAppVersionAndBuildNumber</key>
|
|
||||||
<false/>
|
|
||||||
<key>method</key>
|
|
||||||
<string>app-store-connect</string>
|
|
||||||
<key>signingStyle</key>
|
|
||||||
<string>automatic</string>
|
|
||||||
<key>stripSwiftSymbols</key>
|
|
||||||
<true/>
|
|
||||||
<key>teamID</key>
|
|
||||||
<string>S9YG87C297</string>
|
|
||||||
<key>uploadSymbols</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
# Uncomment this line to define a global platform for your project
|
||||||
platform :ios, '13.0'
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
@ -64,7 +64,7 @@ post_install do |installer|
|
|||||||
## 6. 禁用 Siri/助手 (NSSiriUsageDescription)
|
## 6. 禁用 Siri/助手 (NSSiriUsageDescription)
|
||||||
'PERMISSION_ASSISTANT=0',
|
'PERMISSION_ASSISTANT=0',
|
||||||
|
|
||||||
## 7. 启用 媒体库 (NSAppleMusicUsageDescription)
|
## 7. 禁用 媒体库 (NSAppleMusicUsageDescription)
|
||||||
'PERMISSION_MEDIA_LIBRARY=1',
|
'PERMISSION_MEDIA_LIBRARY=1',
|
||||||
|
|
||||||
## 8. 禁用 蓝牙 (如果你没用到蓝牙,建议也关掉)
|
## 8. 禁用 蓝牙 (如果你没用到蓝牙,建议也关掉)
|
||||||
|
|||||||
@ -11,47 +11,11 @@ PODS:
|
|||||||
- GoogleUtilities/Environment (~> 8.0)
|
- GoogleUtilities/Environment (~> 8.0)
|
||||||
- GoogleUtilities/UserDefaults (~> 8.0)
|
- GoogleUtilities/UserDefaults (~> 8.0)
|
||||||
- PromisesObjC (~> 2.4)
|
- PromisesObjC (~> 2.4)
|
||||||
- audio_session (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- audioplayers_darwin (0.0.1):
|
- audioplayers_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- DKImagePickerController/Core (4.3.9):
|
|
||||||
- DKImagePickerController/ImageDataManager
|
|
||||||
- DKImagePickerController/Resource
|
|
||||||
- DKImagePickerController/ImageDataManager (4.3.9)
|
|
||||||
- DKImagePickerController/PhotoGallery (4.3.9):
|
|
||||||
- DKImagePickerController/Core
|
|
||||||
- DKPhotoGallery
|
|
||||||
- DKImagePickerController/Resource (4.3.9)
|
|
||||||
- DKPhotoGallery (0.0.19):
|
|
||||||
- DKPhotoGallery/Core (= 0.0.19)
|
|
||||||
- DKPhotoGallery/Model (= 0.0.19)
|
|
||||||
- DKPhotoGallery/Preview (= 0.0.19)
|
|
||||||
- DKPhotoGallery/Resource (= 0.0.19)
|
|
||||||
- SDWebImage
|
|
||||||
- SwiftyGif
|
|
||||||
- DKPhotoGallery/Core (0.0.19):
|
|
||||||
- DKPhotoGallery/Model
|
|
||||||
- DKPhotoGallery/Preview
|
|
||||||
- SDWebImage
|
|
||||||
- SwiftyGif
|
|
||||||
- DKPhotoGallery/Model (0.0.19):
|
|
||||||
- SDWebImage
|
|
||||||
- SwiftyGif
|
|
||||||
- DKPhotoGallery/Preview (0.0.19):
|
|
||||||
- DKPhotoGallery/Model
|
|
||||||
- DKPhotoGallery/Resource
|
|
||||||
- SDWebImage
|
|
||||||
- SwiftyGif
|
|
||||||
- DKPhotoGallery/Resource (0.0.19):
|
|
||||||
- SDWebImage
|
|
||||||
- SwiftyGif
|
|
||||||
- file_picker (0.0.1):
|
|
||||||
- DKImagePickerController/PhotoGallery
|
|
||||||
- Flutter
|
|
||||||
- Firebase/Auth (11.15.0):
|
- Firebase/Auth (11.15.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAuth (~> 11.15.0)
|
- FirebaseAuth (~> 11.15.0)
|
||||||
@ -177,9 +141,6 @@ PODS:
|
|||||||
- in_app_purchase_storekit (0.0.1):
|
- in_app_purchase_storekit (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- just_audio (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- libpag (4.5.52)
|
- libpag (4.5.52)
|
||||||
- libwebp (1.5.0):
|
- libwebp (1.5.0):
|
||||||
- libwebp/demux (= 1.5.0)
|
- libwebp/demux (= 1.5.0)
|
||||||
@ -209,9 +170,6 @@ PODS:
|
|||||||
- pag (0.0.1):
|
- pag (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- libpag
|
- libpag
|
||||||
- path_provider_foundation (0.0.1):
|
|
||||||
- Flutter
|
|
||||||
- FlutterMacOS
|
|
||||||
- permission_handler_apple (9.3.0):
|
- permission_handler_apple (9.3.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- PromisesObjC (2.4.0)
|
- PromisesObjC (2.4.0)
|
||||||
@ -233,7 +191,6 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- SwiftyBeaver (1.9.5)
|
- SwiftyBeaver (1.9.5)
|
||||||
- SwiftyGif (5.4.5)
|
|
||||||
- tancent_vap (0.0.1):
|
- tancent_vap (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- tencent_cloud_chat_sdk (8.0.0):
|
- tencent_cloud_chat_sdk (8.0.0):
|
||||||
@ -266,10 +223,8 @@ PODS:
|
|||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
|
||||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`)
|
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
|
||||||
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
|
||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
|
- firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`)
|
||||||
@ -280,11 +235,9 @@ DEPENDENCIES:
|
|||||||
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
|
||||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||||
- just_audio (from `.symlinks/plugins/just_audio/darwin`)
|
|
||||||
- on_audio_query_ios (from `.symlinks/plugins/on_audio_query_ios/ios`)
|
- on_audio_query_ios (from `.symlinks/plugins/on_audio_query_ios/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- pag (from `.symlinks/plugins/pag/ios`)
|
- pag (from `.symlinks/plugins/pag/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
|
||||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
|
||||||
@ -302,8 +255,6 @@ SPEC REPOS:
|
|||||||
trunk:
|
trunk:
|
||||||
- AppAuth
|
- AppAuth
|
||||||
- AppCheckCore
|
- AppCheckCore
|
||||||
- DKImagePickerController
|
|
||||||
- DKPhotoGallery
|
|
||||||
- Firebase
|
- Firebase
|
||||||
- FirebaseAppCheckInterop
|
- FirebaseAppCheckInterop
|
||||||
- FirebaseAuth
|
- FirebaseAuth
|
||||||
@ -331,7 +282,6 @@ SPEC REPOS:
|
|||||||
- SDWebImage
|
- SDWebImage
|
||||||
- SDWebImageWebPCoder
|
- SDWebImageWebPCoder
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
- SwiftyGif
|
|
||||||
- TOCropViewController
|
- TOCropViewController
|
||||||
- TXCustomBeautyProcesserPlugin
|
- TXCustomBeautyProcesserPlugin
|
||||||
- TXIMSDK_Plus_iOS_XCFramework
|
- TXIMSDK_Plus_iOS_XCFramework
|
||||||
@ -340,14 +290,10 @@ SPEC REPOS:
|
|||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
app_links:
|
app_links:
|
||||||
:path: ".symlinks/plugins/app_links/ios"
|
:path: ".symlinks/plugins/app_links/ios"
|
||||||
audio_session:
|
|
||||||
:path: ".symlinks/plugins/audio_session/ios"
|
|
||||||
audioplayers_darwin:
|
audioplayers_darwin:
|
||||||
:path: ".symlinks/plugins/audioplayers_darwin/darwin"
|
:path: ".symlinks/plugins/audioplayers_darwin/darwin"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
file_picker:
|
|
||||||
:path: ".symlinks/plugins/file_picker/ios"
|
|
||||||
firebase_auth:
|
firebase_auth:
|
||||||
:path: ".symlinks/plugins/firebase_auth/ios"
|
:path: ".symlinks/plugins/firebase_auth/ios"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
@ -368,16 +314,12 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||||
in_app_purchase_storekit:
|
in_app_purchase_storekit:
|
||||||
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
|
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
|
||||||
just_audio:
|
|
||||||
:path: ".symlinks/plugins/just_audio/darwin"
|
|
||||||
on_audio_query_ios:
|
on_audio_query_ios:
|
||||||
:path: ".symlinks/plugins/on_audio_query_ios/ios"
|
:path: ".symlinks/plugins/on_audio_query_ios/ios"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
pag:
|
pag:
|
||||||
:path: ".symlinks/plugins/pag/ios"
|
:path: ".symlinks/plugins/pag/ios"
|
||||||
path_provider_foundation:
|
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
|
||||||
permission_handler_apple:
|
permission_handler_apple:
|
||||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
@ -407,12 +349,8 @@ SPEC CHECKSUMS:
|
|||||||
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
|
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
|
||||||
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73
|
||||||
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f
|
||||||
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
|
|
||||||
audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5
|
audioplayers_darwin: 835ced6edd4c9fc8ebb0a7cc9e294a91d99917d5
|
||||||
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
||||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
|
||||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
|
||||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
|
||||||
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
Firebase: d99ac19b909cd2c548339c2241ecd0d1599ab02e
|
||||||
firebase_auth: 50af8366c87bb88c80ebeae62eb60189c7246b9b
|
firebase_auth: 50af8366c87bb88c80ebeae62eb60189c7246b9b
|
||||||
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
|
firebase_core: 995454a784ff288be5689b796deb9e9fa3601818
|
||||||
@ -440,7 +378,6 @@ SPEC CHECKSUMS:
|
|||||||
image_cropper: 655b3ba703c9e15e3111e79151624d6154288774
|
image_cropper: 655b3ba703c9e15e3111e79151624d6154288774
|
||||||
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
|
||||||
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
|
in_app_purchase_storekit: 22cca7d08eebca9babdf4d07d0baccb73325d3c8
|
||||||
just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed
|
|
||||||
libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4
|
libpag: 98742fad4b3ac2a2ee31e383d2483495fb9a5fb4
|
||||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||||
@ -448,7 +385,6 @@ SPEC CHECKSUMS:
|
|||||||
on_audio_query_ios: 28a780e2d0d85d92d500ba6e12c6c8167022b2fa
|
on_audio_query_ios: 28a780e2d0d85d92d500ba6e12c6c8167022b2fa
|
||||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
pag: f01aa9017ab0e04a83ba4a20d6070a50f0ac9da8
|
pag: f01aa9017ab0e04a83ba4a20d6070a50f0ac9da8
|
||||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
|
||||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||||
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
|
||||||
@ -459,7 +395,6 @@ SPEC CHECKSUMS:
|
|||||||
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
|
sign_in_with_apple: c5dcc141574c8c54d5ac99dd2163c0c72ad22418
|
||||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||||
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
|
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
|
||||||
tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68
|
tancent_vap: fa8ad93814a9f950514a7074c662d2c937084c68
|
||||||
tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790
|
tencent_cloud_chat_sdk: 55e5fffe20f6b7937a26a674ccccb639563a9790
|
||||||
tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc
|
tencent_rtc_sdk: f77558b6b436a149d378557c2a837f73c09061bc
|
||||||
@ -468,11 +403,11 @@ SPEC CHECKSUMS:
|
|||||||
TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2
|
TXIMSDK_Plus_iOS_XCFramework: 5d1933192fb3b7ef2fe933f1623de4a0486a7fe2
|
||||||
TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c
|
TXLiteAVSDK_Professional: e792e6084a208ecef6196c5f28237ad8078b4d7c
|
||||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52
|
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||||
webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
|
webview_flutter_wkwebview: 8ebf4fded22593026f7dbff1fbff31ea98573c8d
|
||||||
|
|
||||||
PODFILE CHECKSUM: b724feb0f01aec7228c34cc4763485e911aa9322
|
PODFILE CHECKSUM: a6f49a93e5f85201a2efdadcd7cf184b0e310894
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@ -18,7 +18,6 @@
|
|||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
B7A1D58E2EE51A0000000001 /* GiftMp4VideoPlatformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A1D58F2EE51A0000000002 /* GiftMp4VideoPlatformView.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -71,7 +70,6 @@
|
|||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
B7A1D58F2EE51A0000000002 /* GiftMp4VideoPlatformView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiftMp4VideoPlatformView.swift; sourceTree = "<group>"; };
|
|
||||||
D8CD6B2D6477A38E56A72C61 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D8CD6B2D6477A38E56A72C61 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -172,7 +170,6 @@
|
|||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||||
B7A1D58F2EE51A0000000002 /* GiftMp4VideoPlatformView.swift */,
|
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||||
);
|
);
|
||||||
path = Runner;
|
path = Runner;
|
||||||
@ -213,7 +210,6 @@
|
|||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
F1AFD9E9E8DBA4F1609CE22F /* [CP] Embed Pods Frameworks */,
|
F1AFD9E9E8DBA4F1609CE22F /* [CP] Embed Pods Frameworks */,
|
||||||
5DF67264ADC5268AC3021CED /* [CP] Copy Pods Resources */,
|
5DF67264ADC5268AC3021CED /* [CP] Copy Pods Resources */,
|
||||||
B7A1D5902EE51A0000000003 /* Upload Crashlytics Symbols */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -356,22 +352,6 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
B7A1D5902EE51A0000000003 /* Upload Crashlytics Symbols */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}",
|
|
||||||
"${PROJECT_DIR}/Runner/GoogleService-Info.plist",
|
|
||||||
);
|
|
||||||
name = "Upload Crashlytics Symbols";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "if [ \"$CONFIGURATION\" = \"Release\" ] || [ \"$CONFIGURATION\" = \"Profile\" ]; then\n if [ -x \"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols\" ]; then\n \"${PODS_ROOT}/FirebaseCrashlytics/upload-symbols\" -gsp \"${PROJECT_DIR}/Runner/GoogleService-Info.plist\" -p ios \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"\n else\n echo \"warning: FirebaseCrashlytics upload-symbols not found at ${PODS_ROOT}/FirebaseCrashlytics/upload-symbols\"\n fi\nfi\n";
|
|
||||||
};
|
|
||||||
CFD1FCCAE92AFBF15E8B607E /* [CP] Check Pods Manifest.lock */ = {
|
CFD1FCCAE92AFBF15E8B607E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -427,7 +407,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||||
B7A1D58E2EE51A0000000001 /* GiftMp4VideoPlatformView.swift in Sources */,
|
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -510,7 +489,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
@ -519,13 +498,12 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPat_icon_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
|
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 102;
|
CURRENT_PROJECT_VERSION = 140;
|
||||||
DEVELOPMENT_TEAM = S9YG87C297;
|
DEVELOPMENT_TEAM = S9X2AJ2US9;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
@ -533,7 +511,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -647,7 +625,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@ -701,7 +679,7 @@
|
|||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
TARGETED_DEVICE_FAMILY = 1;
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@ -710,13 +688,13 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPat_icon_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 102;
|
CURRENT_PROJECT_VERSION = 140;
|
||||||
DEVELOPMENT_TEAM = S9YG87C297;
|
DEVELOPMENT_TEAM = F33K8VUZ62;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
@ -724,7 +702,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -739,13 +717,13 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPat_icon_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 102;
|
CURRENT_PROJECT_VERSION = 140;
|
||||||
DEVELOPMENT_TEAM = S9YG87C297;
|
DEVELOPMENT_TEAM = F33K8VUZ62;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
@ -753,7 +731,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
PRODUCT_BUNDLE_IDENTIFIER = com.org.yumiparty;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
@ -9,23 +9,11 @@ import UIKit
|
|||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
registerGiftMp4VideoView()
|
|
||||||
registerDurableAuthStorageChannel()
|
registerDurableAuthStorageChannel()
|
||||||
registerMobileContextChannel()
|
registerMobileContextChannel()
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func registerGiftMp4VideoView() {
|
|
||||||
guard let registrar = self.registrar(forPlugin: "GiftMp4VideoViewPlugin") else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
GiftMp4VideoPlatformView.installControlChannel(messenger: registrar.messenger())
|
|
||||||
registrar.register(
|
|
||||||
GiftMp4VideoViewFactory(messenger: registrar.messenger()),
|
|
||||||
withId: GiftMp4VideoPlatformView.viewType
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func registerMobileContextChannel() {
|
private func registerMobileContextChannel() {
|
||||||
guard let controller = window?.rootViewController as? FlutterViewController else {
|
guard let controller = window?.rootViewController as? FlutterViewController else {
|
||||||
return
|
return
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 67 KiB |
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "LaunchBackground.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "LaunchBackground@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "LaunchBackground@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 811 KiB |
|
Before Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 68 B |
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "LaunchLogo.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "LaunchLogo@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "LaunchLogo@3x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 108 KiB |
@ -16,23 +16,13 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="BGD-k0-Ey4">
|
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||||
</imageView>
|
|
||||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFit" image="LaunchLogo" translatesAutoresizingMaskIntoConstraints="NO" id="LOG-k0-Ey4">
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="107" id="LOG-width-107"/>
|
|
||||||
<constraint firstAttribute="height" constant="97" id="LOG-height-97"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="0" green="0" blue="0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="BGD-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="BGD-top"/>
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||||
<constraint firstItem="BGD-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="BGD-leading"/>
|
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||||
<constraint firstItem="BGD-k0-Ey4" firstAttribute="trailing" secondItem="Ze5-6b-2t3" secondAttribute="trailing" id="BGD-trailing"/>
|
|
||||||
<constraint firstItem="BGD-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="BGD-bottom"/>
|
|
||||||
<constraint firstItem="LOG-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="LOG-centerX"/>
|
|
||||||
<constraint firstItem="LOG-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="LOG-centerY"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
@ -42,7 +32,6 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="LaunchBackground" width="375" height="812"/>
|
<image name="LaunchImage" width="168" height="185"/>
|
||||||
<image name="LaunchLogo" width="107" height="97"/>
|
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -38,22 +38,8 @@
|
|||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.googleusercontent.apps.52796773508-dk441c9e7tdmqv47mbdt2vpbn2clrii8</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.googleusercontent.apps.52796773508-dk441c9e7tdmqv47mbdt2vpbn2clrii8</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.org.yumiparty</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Editor</string>
|
<string>Editor</string>
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>yumi</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
@ -64,8 +50,6 @@
|
|||||||
<string>We need access to your camera so you can take a photo and make it a profile picture, or take and share a picture in real time in a voice chat room.</string>
|
<string>We need access to your camera so you can take a photo and make it a profile picture, or take and share a picture in real time in a voice chat room.</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<string>We need access to your microphone so that you can make live calls, record, and send voice messages with other users in the voice dating room.</string>
|
<string>We need access to your microphone so that you can make live calls, record, and send voice messages with other users in the voice dating room.</string>
|
||||||
<key>NSAppleMusicUsageDescription</key>
|
|
||||||
<string>We need access to your media library so you can select local music to play in voice rooms.</string>
|
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<string>We need your permission to save beautiful images you take or edit in the app to your phone album.</string>
|
<string>We need your permission to save beautiful images you take or edit in the app to your phone album.</string>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
@ -93,5 +77,9 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>audio</string>
|
<string>audio</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>com.apple.developer.applesignin</key>
|
||||||
|
<array>
|
||||||
|
<string>Default</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -6,9 +6,5 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>Default</string>
|
<string>Default</string>
|
||||||
</array>
|
</array>
|
||||||
<key>com.apple.developer.associated-domains</key>
|
|
||||||
<array>
|
|
||||||
<string>applinks:h5.yumi.com</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -6,9 +6,5 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>Default</string>
|
<string>Default</string>
|
||||||
</array>
|
</array>
|
||||||
<key>com.apple.developer.associated-domains</key>
|
|
||||||
<array>
|
|
||||||
<string>applinks:h5.yumi.com</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -16,8 +16,8 @@ class SCVariant1Config implements AppConfig {
|
|||||||
@override
|
@override
|
||||||
String get apiHost => const String.fromEnvironment(
|
String get apiHost => const String.fromEnvironment(
|
||||||
'API_HOST',
|
'API_HOST',
|
||||||
defaultValue: 'http://192.168.110.64:1100/',
|
defaultValue: 'http://192.168.110.66:1100/',
|
||||||
); // 默认连本地接口,本地调试可通过 --dart-define=API_HOST 覆盖,线上“https://jvapi.haiyihy.com/”
|
); // 默认连线上环境,本地调试可通过 --dart-define=API_HOST 覆盖 本地“http://192.168.110.66:1100/”,线上“https://jvapi.haiyihy.com/”
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get imgHost => 'https://img.atuchat.com/'; // 测试图片服务器,上架前需替换为正式域名
|
String get imgHost => 'https://img.atuchat.com/'; // 测试图片服务器,上架前需替换为正式域名
|
||||||
|
|||||||
@ -168,7 +168,8 @@ class SCGlobalConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String clampFloatingBroadcastScope(String value) {
|
static String clampFloatingBroadcastScope(String value) {
|
||||||
return normalizeFloatingBroadcastScope(value);
|
final scope = normalizeFloatingBroadcastScope(value);
|
||||||
|
return _isLowPerformanceDevice ? floatingBroadcastScopeOff : scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
static String floatingBroadcastScopeStorageKey(String? account) {
|
static String floatingBroadcastScopeStorageKey(String? account) {
|
||||||
@ -187,7 +188,8 @@ class SCGlobalConfig {
|
|||||||
|
|
||||||
static bool get isLowPerformanceDevice => _isLowPerformanceDevice;
|
static bool get isLowPerformanceDevice => _isLowPerformanceDevice;
|
||||||
|
|
||||||
static bool get allowsHighCostAnimations => true;
|
static bool get allowsHighCostAnimations =>
|
||||||
|
!_isLowPerformanceDevice && sdkInt > maxSdkNoAnim;
|
||||||
|
|
||||||
static int get recommendedImageCacheBytes =>
|
static int get recommendedImageCacheBytes =>
|
||||||
_isLowPerformanceDevice ? 40 * 1024 * 1024 : 100 * 1024 * 1024;
|
_isLowPerformanceDevice ? 40 * 1024 * 1024 : 100 * 1024 * 1024;
|
||||||
@ -209,17 +211,19 @@ class SCGlobalConfig {
|
|||||||
isLowRamDevice ||
|
isLowRamDevice ||
|
||||||
processorCount <= 6 ||
|
processorCount <= 6 ||
|
||||||
matchesKnownLowPerformanceModel;
|
matchesKnownLowPerformanceModel;
|
||||||
|
resetVisualEffectSwitchesToRecommendedDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool clampVisualEffectPreference(bool enabled) {
|
static bool clampVisualEffectPreference(bool enabled) {
|
||||||
return enabled;
|
return !_isLowPerformanceDevice && enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resetVisualEffectSwitchesToRecommendedDefaults() {
|
static void resetVisualEffectSwitchesToRecommendedDefaults() {
|
||||||
isEntryVehicleAnimation = true;
|
final enabled = !_isLowPerformanceDevice;
|
||||||
isGiftSpecialEffects = true;
|
isEntryVehicleAnimation = enabled;
|
||||||
isFloatingAnimationInGlobal = true;
|
isGiftSpecialEffects = enabled;
|
||||||
isLuckGiftSpecialEffects = true;
|
isFloatingAnimationInGlobal = enabled;
|
||||||
|
isLuckGiftSpecialEffects = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取当前业务逻辑策略
|
/// 获取当前业务逻辑策略
|
||||||
|
|||||||
@ -3,7 +3,6 @@ class SCRoomMsgType {
|
|||||||
static const String image = "IMAGE";
|
static const String image = "IMAGE";
|
||||||
static const String joinRoom = "JOIN_ROOM";
|
static const String joinRoom = "JOIN_ROOM";
|
||||||
static const String systemTips = "SYSTEM_TIPS";
|
static const String systemTips = "SYSTEM_TIPS";
|
||||||
static const String cpSystemNotice = "CP_SYSTEM_NOTICE";
|
|
||||||
|
|
||||||
///表情包
|
///表情包
|
||||||
static const String emoticons = "EMOTICONS";
|
static const String emoticons = "EMOTICONS";
|
||||||
|
|||||||
@ -890,8 +890,6 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get bio => translate('bio');
|
String get bio => translate('bio');
|
||||||
|
|
||||||
String get introduction => translate('introduction');
|
|
||||||
|
|
||||||
String get hobby => translate('hobby');
|
String get hobby => translate('hobby');
|
||||||
|
|
||||||
String get search => translate('search');
|
String get search => translate('search');
|
||||||
@ -997,47 +995,6 @@ class SCAppLocalizations {
|
|||||||
String get confirmDeclineTheInvitation =>
|
String get confirmDeclineTheInvitation =>
|
||||||
translate('confirmDeclineTheInvitation');
|
translate('confirmDeclineTheInvitation');
|
||||||
|
|
||||||
String cpInviteRejectedByOther(String relation) =>
|
|
||||||
translate('cpInviteRejectedByOther').replaceAll('{1}', relation);
|
|
||||||
|
|
||||||
String get closeFriendInvitationTitle =>
|
|
||||||
translate('closeFriendInvitationTitle');
|
|
||||||
|
|
||||||
String get closeFriendInvitationSubtitle =>
|
|
||||||
translate('closeFriendInvitationSubtitle');
|
|
||||||
|
|
||||||
String get closeFriendInvitationExpired =>
|
|
||||||
translate('closeFriendInvitationExpired');
|
|
||||||
|
|
||||||
String get closeFriendRequestDeclined =>
|
|
||||||
translate('closeFriendRequestDeclined');
|
|
||||||
|
|
||||||
String get closeFriendsHaveBecome => translate('closeFriendsHaveBecome');
|
|
||||||
|
|
||||||
String get otherAcceptedCloseFriendInvite =>
|
|
||||||
translate('otherAcceptedCloseFriendInvite');
|
|
||||||
|
|
||||||
String get otherDeclinedCloseFriendInvite =>
|
|
||||||
translate('otherDeclinedCloseFriendInvite');
|
|
||||||
|
|
||||||
String get youAcceptedCloseFriendInvite =>
|
|
||||||
translate('youAcceptedCloseFriendInvite');
|
|
||||||
|
|
||||||
String get youDeclinedCloseFriendInvite =>
|
|
||||||
translate('youDeclinedCloseFriendInvite');
|
|
||||||
|
|
||||||
String get closeFriendInviteTimedOut =>
|
|
||||||
translate('closeFriendInviteTimedOut');
|
|
||||||
|
|
||||||
String cpDismissConfirmMessage(String coins) =>
|
|
||||||
translate('cpDismissConfirmMessage').replaceAll('{1}', coins);
|
|
||||||
|
|
||||||
String get cpRelationCp => translate('cpRelationCp');
|
|
||||||
|
|
||||||
String get cpRelationBrother => translate('cpRelationBrother');
|
|
||||||
|
|
||||||
String get cpRelationSister => translate('cpRelationSister');
|
|
||||||
|
|
||||||
String get propMessagePrompt => translate('propMessagePrompt');
|
String get propMessagePrompt => translate('propMessagePrompt');
|
||||||
|
|
||||||
String get chats => translate('chats');
|
String get chats => translate('chats');
|
||||||
@ -1595,9 +1552,6 @@ class SCAppLocalizations {
|
|||||||
|
|
||||||
String get kickRoomTips => translate('kickRoomTips');
|
String get kickRoomTips => translate('kickRoomTips');
|
||||||
|
|
||||||
String get roomKickedEntryBlockedTips =>
|
|
||||||
translate('roomKickedEntryBlockedTips');
|
|
||||||
|
|
||||||
String get joinRoomTips => translate('joinRoomTips');
|
String get joinRoomTips => translate('joinRoomTips');
|
||||||
|
|
||||||
String get youAreCurrentlyCPRelationshipPleaseDissolve =>
|
String get youAreCurrentlyCPRelationshipPleaseDissolve =>
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/first_recharge/first_recharge_dialog.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
|
||||||
runApp(const FirstRechargeDialogPreviewApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class FirstRechargeDialogPreviewApp extends StatelessWidget {
|
|
||||||
const FirstRechargeDialogPreviewApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ScreenUtilInit(
|
|
||||||
designSize: const Size(375, 812),
|
|
||||||
minTextAdapt: true,
|
|
||||||
splitScreenMode: true,
|
|
||||||
builder:
|
|
||||||
(_, child) => MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
builder: FlutterSmartDialog.init(),
|
|
||||||
home: const _FirstRechargeDialogPreviewPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FirstRechargeDialogPreviewPage extends StatefulWidget {
|
|
||||||
const _FirstRechargeDialogPreviewPage();
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_FirstRechargeDialogPreviewPage> createState() =>
|
|
||||||
_FirstRechargeDialogPreviewPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FirstRechargeDialogPreviewPageState
|
|
||||||
extends State<_FirstRechargeDialogPreviewPage> {
|
|
||||||
bool _shown = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
if (_shown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_shown = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FirstRechargeDialog.show(
|
|
||||||
context,
|
|
||||||
data: FirstRechargeDialogData.mock(),
|
|
||||||
onBuyNow: (_) {},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.black,
|
|
||||||
body: Image.asset(
|
|
||||||
'sc_images/first_recharge/preview_background.png',
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,173 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:yumi/app/config/app_config.dart';
|
|
||||||
import 'package:yumi/app_localizations.dart';
|
|
||||||
import 'package:yumi/main.dart';
|
|
||||||
import 'package:yumi/modules/gift/gift_page.dart';
|
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
AppConfig.initialize();
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
await DataPersistence.initialize();
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
|
||||||
runApp(const GiftPagePreviewApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class GiftPagePreviewApp extends StatelessWidget {
|
|
||||||
const GiftPagePreviewApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MultiProvider(
|
|
||||||
providers: [
|
|
||||||
ChangeNotifierProvider<SCAppGeneralManager>(
|
|
||||||
create: (_) => _PreviewAppGeneralManager(),
|
|
||||||
),
|
|
||||||
ChangeNotifierProvider<RtcProvider>(create: (_) => RtcProvider()),
|
|
||||||
ChangeNotifierProvider<RtmProvider>(create: (_) => RtmProvider()),
|
|
||||||
ChangeNotifierProvider<SocialChatUserProfileManager>(
|
|
||||||
create: (_) => _PreviewUserProfileManager(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: ScreenUtilInit(
|
|
||||||
designSize: const Size(375, 812),
|
|
||||||
minTextAdapt: true,
|
|
||||||
splitScreenMode: true,
|
|
||||||
builder:
|
|
||||||
(_, child) => MaterialApp(
|
|
||||||
navigatorKey: navigatorKey,
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
locale: const Locale('en'),
|
|
||||||
supportedLocales: SCAppLocalizations.supportedLocales,
|
|
||||||
localizationsDelegates: const [
|
|
||||||
SCAppLocalizations.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
builder: FlutterSmartDialog.init(),
|
|
||||||
home: const _GiftPagePreviewHome(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _GiftPagePreviewHome extends StatefulWidget {
|
|
||||||
const _GiftPagePreviewHome();
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_GiftPagePreviewHome> createState() => _GiftPagePreviewHomeState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _GiftPagePreviewHomeState extends State<_GiftPagePreviewHome> {
|
|
||||||
bool _shown = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
if (_shown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_shown = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SmartDialog.show(
|
|
||||||
tag: "showGiftControl",
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
maskColor: Colors.transparent,
|
|
||||||
animationType: SmartAnimationType.fade,
|
|
||||||
clickMaskDismiss: true,
|
|
||||||
builder: (_) => const GiftPage(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.black,
|
|
||||||
body: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
'sc_images/room/sc_icon_room_defaut_bg.png',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder:
|
|
||||||
(_, __, ___) => const ColoredBox(color: Color(0xFF062320)),
|
|
||||||
),
|
|
||||||
const Align(
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(top: 88),
|
|
||||||
child: Text(
|
|
||||||
'Gift panel preview',
|
|
||||||
style: TextStyle(color: Colors.white70, fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewAppGeneralManager extends SCAppGeneralManager {
|
|
||||||
_PreviewAppGeneralManager() {
|
|
||||||
final gifts = List<SocialChatGiftRes>.generate(
|
|
||||||
8,
|
|
||||||
(index) => SocialChatGiftRes(
|
|
||||||
id: 'preview_$index',
|
|
||||||
giftName: 'Gift ${index + 1}',
|
|
||||||
giftCandy: (index + 1) * 10,
|
|
||||||
giftTab: 'ALL',
|
|
||||||
giftPhoto: '',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
giftResList = gifts;
|
|
||||||
giftByTab = {'ALL': gifts};
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> fetchCountryList() async {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> giftList({
|
|
||||||
bool includeCustomized = true,
|
|
||||||
bool forceRefresh = false,
|
|
||||||
}) async {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void giftActivityList() {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> giftBackpack({bool forceRefresh = false}) async {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> warmGiftPageCovers(
|
|
||||||
String type, {
|
|
||||||
required int pageIndex,
|
|
||||||
int pageCount = 1,
|
|
||||||
int pageSize = 8,
|
|
||||||
}) async {}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool isGiftTabLoading(String type) => false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewUserProfileManager extends SocialChatUserProfileManager {
|
|
||||||
@override
|
|
||||||
void balance() {}
|
|
||||||
}
|
|
||||||
@ -1,164 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:yumi/app_localizations.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_animation_switch_dialog.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
SCGlobalConfig.isGiftSpecialEffects = true;
|
|
||||||
SCGlobalConfig.isLuckGiftSpecialEffects = true;
|
|
||||||
SCGlobalConfig.isEntryVehicleAnimation = false;
|
|
||||||
runApp(const RoomAnimationSwitchDialogPreviewApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomAnimationSwitchDialogPreviewApp extends StatelessWidget {
|
|
||||||
const RoomAnimationSwitchDialogPreviewApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ScreenUtilInit(
|
|
||||||
designSize: const Size(375, 812),
|
|
||||||
minTextAdapt: true,
|
|
||||||
splitScreenMode: true,
|
|
||||||
builder:
|
|
||||||
(_, child) => MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
locale: const Locale('en'),
|
|
||||||
supportedLocales: SCAppLocalizations.supportedLocales,
|
|
||||||
localizationsDelegates: const [
|
|
||||||
SCAppLocalizations.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
builder: FlutterSmartDialog.init(),
|
|
||||||
home: const _RoomAnimationSwitchDialogPreviewPage(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RoomAnimationSwitchDialogPreviewPage extends StatelessWidget {
|
|
||||||
const _RoomAnimationSwitchDialogPreviewPage();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.black,
|
|
||||||
body: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: const [
|
|
||||||
_PreviewRoomBackground(),
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: RoomAnimationSwitchDialog(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewRoomBackground extends StatelessWidget {
|
|
||||||
const _PreviewRoomBackground();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors: [Color(0xFF172F35), Color(0xFF071B17), Color(0xFF1B1308)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
left: 24.w,
|
|
||||||
top: 70.w,
|
|
||||||
child: _PreviewLine(width: 128.w, opacity: 0.55),
|
|
||||||
),
|
|
||||||
for (var index = 0; index < 10; index++)
|
|
||||||
Positioned(
|
|
||||||
left: (38 + (index % 5) * 66).w,
|
|
||||||
top: (145 + (index ~/ 5) * 80).w,
|
|
||||||
child: const _PreviewSeat(),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 26.w,
|
|
||||||
right: 26.w,
|
|
||||||
top: 355.w,
|
|
||||||
child: _PreviewPanel(height: 150.w),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 24.w,
|
|
||||||
right: 24.w,
|
|
||||||
bottom: 36.w,
|
|
||||||
child: _PreviewPanel(height: 52.w),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewSeat extends StatelessWidget {
|
|
||||||
const _PreviewSeat();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFFFFCE7C).withValues(alpha: 0.36),
|
|
||||||
width: 2.w,
|
|
||||||
),
|
|
||||||
color: Colors.black.withValues(alpha: 0.18),
|
|
||||||
),
|
|
||||||
child: SizedBox(width: 44.w, height: 44.w),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewLine extends StatelessWidget {
|
|
||||||
const _PreviewLine({required this.width, required this.opacity});
|
|
||||||
|
|
||||||
final double width;
|
|
||||||
final double opacity;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: width,
|
|
||||||
height: 14.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withValues(alpha: opacity),
|
|
||||||
borderRadius: BorderRadius.circular(7.w),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewPanel extends StatelessWidget {
|
|
||||||
const _PreviewPanel({required this.height});
|
|
||||||
|
|
||||||
final double height;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
height: height,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black.withValues(alpha: 0.24),
|
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
|
||||||
border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -48,7 +48,7 @@ class _RoomCpProgressDialogPreviewPage extends StatelessWidget {
|
|||||||
const RoomCpProgressDialog(
|
const RoomCpProgressDialog(
|
||||||
daysValue: '999',
|
daysValue: '999',
|
||||||
daysLabel: 'Days',
|
daysLabel: 'Days',
|
||||||
levelText: 'Lv.0',
|
levelText: 'Lv.1 Simple Love',
|
||||||
currentValue: 1,
|
currentValue: 1,
|
||||||
targetValue: 1,
|
targetValue: 1,
|
||||||
expAwayText: '1,000',
|
expAwayText: '1,000',
|
||||||
|
|||||||
@ -1,113 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/cp/room_cp_relation_formed_effect.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
runApp(const RoomCpRelationFormedEffectPreviewApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomCpRelationFormedEffectPreviewApp extends StatelessWidget {
|
|
||||||
const RoomCpRelationFormedEffectPreviewApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return ScreenUtilInit(
|
|
||||||
designSize: Size(SCScreen.designWidth, SCScreen.designHeight),
|
|
||||||
splitScreenMode: false,
|
|
||||||
minTextAdapt: true,
|
|
||||||
builder: (context, child) {
|
|
||||||
return MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
builder: FlutterSmartDialog.init(),
|
|
||||||
home: const RoomCpRelationFormedEffectPreviewPage(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomCpRelationFormedEffectPreviewPage extends StatefulWidget {
|
|
||||||
const RoomCpRelationFormedEffectPreviewPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<RoomCpRelationFormedEffectPreviewPage> createState() =>
|
|
||||||
_RoomCpRelationFormedEffectPreviewPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RoomCpRelationFormedEffectPreviewPageState
|
|
||||||
extends State<RoomCpRelationFormedEffectPreviewPage> {
|
|
||||||
Timer? _loopTimer;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _showEffect());
|
|
||||||
_loopTimer = Timer.periodic(
|
|
||||||
const Duration(seconds: 7),
|
|
||||||
(_) => _showEffect(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_loopTimer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showEffect() {
|
|
||||||
RoomCpRelationFormedEffect.show(
|
|
||||||
leftName: "Avery",
|
|
||||||
rightName: "Jordan",
|
|
||||||
leftAvatar: "https://i.pravatar.cc/180?img=32",
|
|
||||||
rightAvatar: "https://i.pravatar.cc/180?img=47",
|
|
||||||
relationType: "CP",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xFF170D24),
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors: [Color(0xFF2B163A), Color(0xFF07040D)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 16,
|
|
||||||
right: 16,
|
|
||||||
bottom: 34,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
FilledButton(
|
|
||||||
onPressed: _showEffect,
|
|
||||||
child: const Text("Replay"),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
FilledButton.tonal(
|
|
||||||
onPressed:
|
|
||||||
() => SmartDialog.dismiss(
|
|
||||||
tag: RoomCpRelationFormedEffect.dialogTag,
|
|
||||||
),
|
|
||||||
child: const Text("Dismiss"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:yumi/app_localizations.dart';
|
|
||||||
import 'package:yumi/main.dart';
|
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
|
||||||
import 'package:yumi/services/payment/google_payment_manager.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_recharge_bottom_sheet.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
|
||||||
runApp(const RoomRechargeBottomSheetPreviewApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
class RoomRechargeBottomSheetPreviewApp extends StatelessWidget {
|
|
||||||
const RoomRechargeBottomSheetPreviewApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MultiProvider(
|
|
||||||
providers: [
|
|
||||||
ChangeNotifierProvider<SocialChatUserProfileManager>(
|
|
||||||
create: (_) => _PreviewUserProfileManager(),
|
|
||||||
),
|
|
||||||
ChangeNotifierProvider<AndroidPaymentProcessor>(
|
|
||||||
create: (_) => AndroidPaymentProcessor(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: ScreenUtilInit(
|
|
||||||
designSize: const Size(375, 812),
|
|
||||||
minTextAdapt: true,
|
|
||||||
splitScreenMode: true,
|
|
||||||
builder:
|
|
||||||
(_, child) => MaterialApp(
|
|
||||||
navigatorKey: navigatorKey,
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
locale: const Locale('en'),
|
|
||||||
supportedLocales: SCAppLocalizations.supportedLocales,
|
|
||||||
localizationsDelegates: const [
|
|
||||||
SCAppLocalizations.delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
],
|
|
||||||
builder: FlutterSmartDialog.init(),
|
|
||||||
home: const _RoomRechargeBottomSheetPreviewPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RoomRechargeBottomSheetPreviewPage extends StatefulWidget {
|
|
||||||
const _RoomRechargeBottomSheetPreviewPage();
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_RoomRechargeBottomSheetPreviewPage> createState() =>
|
|
||||||
_RoomRechargeBottomSheetPreviewPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RoomRechargeBottomSheetPreviewPageState
|
|
||||||
extends State<_RoomRechargeBottomSheetPreviewPage> {
|
|
||||||
bool _shown = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeDependencies() {
|
|
||||||
super.didChangeDependencies();
|
|
||||||
if (_shown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_shown = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RoomRechargeBottomSheet.show(context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.black,
|
|
||||||
body: Image.asset(
|
|
||||||
'sc_images/room/sc_icon_room_defaut_bg.png',
|
|
||||||
width: double.infinity,
|
|
||||||
height: double.infinity,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PreviewUserProfileManager extends SocialChatUserProfileManager {
|
|
||||||
@override
|
|
||||||
void balance() {
|
|
||||||
updateBalance(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,6 @@ import 'package:firebase_core/firebase_core.dart';
|
|||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:fluro/fluro.dart' as fluro;
|
import 'package:fluro/fluro.dart' as fluro;
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -45,6 +44,8 @@ import 'services/shop/shop_manager.dart';
|
|||||||
import 'services/theme/theme_manager.dart';
|
import 'services/theme/theme_manager.dart';
|
||||||
import 'services/auth/user_profile_manager.dart';
|
import 'services/auth/user_profile_manager.dart';
|
||||||
import 'ui_kit/theme/socialchat_theme.dart';
|
import 'ui_kit/theme/socialchat_theme.dart';
|
||||||
|
import 'ui_kit/widgets/room/anim/room_gift_seat_flight_overlay.dart';
|
||||||
|
import 'ui_kit/widgets/room/effect/vapp_svga_layer_widget.dart';
|
||||||
|
|
||||||
bool _isCrashlyticsReady = false;
|
bool _isCrashlyticsReady = false;
|
||||||
|
|
||||||
@ -277,8 +278,6 @@ class RootAppWithProviders extends StatelessWidget {
|
|||||||
|
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
||||||
final RouteObserver<ModalRoute<dynamic>> modalRouteObserver =
|
|
||||||
RouteObserver<ModalRoute<dynamic>>();
|
|
||||||
|
|
||||||
class YumiApplication extends StatefulWidget {
|
class YumiApplication extends StatefulWidget {
|
||||||
const YumiApplication({super.key});
|
const YumiApplication({super.key});
|
||||||
@ -300,7 +299,6 @@ class _YumiApplicationState extends State<YumiApplication>
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_initRouter();
|
_initRouter();
|
||||||
unawaited(_initLink());
|
unawaited(_initLink());
|
||||||
unawaited(_reconcilePendingGooglePurchases(force: true));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,24 +325,6 @@ class _YumiApplicationState extends State<YumiApplication>
|
|||||||
context.read<RtcProvider>().releaseRtcEngineForAppTermination(),
|
context.read<RtcProvider>().releaseRtcEngineForAppTermination(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (state == AppLifecycleState.resumed) {
|
|
||||||
unawaited(_reconcilePendingGooglePurchases());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _reconcilePendingGooglePurchases({bool force = false}) async {
|
|
||||||
if (!Platform.isAndroid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await context
|
|
||||||
.read<AndroidPaymentProcessor>()
|
|
||||||
.reconcilePendingGooglePurchases(context: context, force: force);
|
|
||||||
} catch (error) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint('reconcile pending google purchases failed: $error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -447,11 +427,46 @@ class _YumiApplicationState extends State<YumiApplication>
|
|||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [child ?? const SizedBox.shrink()],
|
children: [
|
||||||
|
child ?? const SizedBox.shrink(),
|
||||||
|
if (SCGlobalConfig.allowsHighCostAnimations)
|
||||||
|
Consumer<RtcProvider>(
|
||||||
|
builder: (context, rtcProvider, _) {
|
||||||
|
if (!rtcProvider
|
||||||
|
.shouldShowRoomVisualEffects) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return const Positioned.fill(
|
||||||
|
child: VapPlusSvgaPlayer(
|
||||||
|
tag: "room_gift",
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
navigatorObservers: [routeObserver, modalRouteObserver],
|
Consumer<RtcProvider>(
|
||||||
|
builder: (context, rtcProvider, _) {
|
||||||
|
if (!rtcProvider
|
||||||
|
.shouldShowRoomVisualEffects) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Positioned.fill(
|
||||||
|
child: RoomGiftSeatFlightOverlay(
|
||||||
|
controller:
|
||||||
|
RoomGiftSeatFlightController(),
|
||||||
|
resolveTargetKey:
|
||||||
|
(userId) => Provider.of<RtcProvider>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).getSeatGlobalKeyByIndex(userId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
navigatorObservers: [routeObserver],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -106,7 +106,7 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
|||||||
if (success) {
|
if (success) {
|
||||||
userProvider?.updateUserAvatar(url);
|
userProvider?.updateUserAvatar(url);
|
||||||
}
|
}
|
||||||
}, allowGif: true);
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 15.w),
|
SizedBox(height: 15.w),
|
||||||
@ -559,6 +559,24 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
|||||||
return primary ?? fallback;
|
return primary ?? fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isBrokenLocalMediaUrl(String? url) {
|
||||||
|
return (url ?? "").contains("/external/oss/local/");
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _preferUsableAvatar(String? primary, String? fallback) {
|
||||||
|
if (primary != null &&
|
||||||
|
primary.isNotEmpty &&
|
||||||
|
!_isBrokenLocalMediaUrl(primary)) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
if (fallback != null &&
|
||||||
|
fallback.isNotEmpty &&
|
||||||
|
!_isBrokenLocalMediaUrl(fallback)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return _preferNonEmpty(primary, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
num? _preferNonZero(num? primary, num? fallback) {
|
num? _preferNonZero(num? primary, num? fallback) {
|
||||||
if (primary != null && primary != 0) {
|
if (primary != null && primary != 0) {
|
||||||
return primary;
|
return primary;
|
||||||
@ -643,6 +661,10 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
|||||||
if (submittedProfile != null) {
|
if (submittedProfile != null) {
|
||||||
final mergedProfile = (user.userProfile ?? SocialChatUserProfile())
|
final mergedProfile = (user.userProfile ?? SocialChatUserProfile())
|
||||||
.copyWith(
|
.copyWith(
|
||||||
|
userAvatar: _preferUsableAvatar(
|
||||||
|
user.userProfile?.userAvatar,
|
||||||
|
submittedProfile.userAvatar,
|
||||||
|
),
|
||||||
userNickname: _preferNonEmpty(
|
userNickname: _preferNonEmpty(
|
||||||
user.userProfile?.userNickname,
|
user.userProfile?.userNickname,
|
||||||
submittedProfile.userNickname,
|
submittedProfile.userNickname,
|
||||||
|
|||||||
@ -16,26 +16,25 @@ import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/msg/message_conversation_list_page.dart';
|
import 'package:yumi/ui_kit/widgets/msg/message_conversation_list_page.dart';
|
||||||
|
|
||||||
|
import '../../index/main_route.dart';
|
||||||
import '../chat_route.dart';
|
import '../chat_route.dart';
|
||||||
|
|
||||||
///消息
|
///消息
|
||||||
class SCMessagePage extends StatefulWidget {
|
class SCMessagePage extends StatefulWidget {
|
||||||
final bool isFromRoom;
|
bool isFromRoom = false;
|
||||||
|
|
||||||
const SCMessagePage({super.key, this.isFromRoom = false});
|
SCMessagePage({this.isFromRoom = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SCMessagePage> createState() => _MessagePageState();
|
_MessagePageState createState() => _MessagePageState(isFromRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessagePageState extends State<SCMessagePage> {
|
class _MessagePageState extends State<SCMessagePage> {
|
||||||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||||||
|
bool isFromRoom = false;
|
||||||
bool showSystemAnnouncementTips = true;
|
bool showSystemAnnouncementTips = true;
|
||||||
final TextEditingController _messageSearchController =
|
|
||||||
TextEditingController();
|
_MessagePageState(this.isFromRoom);
|
||||||
final FocusNode _messageSearchFocusNode = FocusNode();
|
|
||||||
bool _isSearchingMessages = false;
|
|
||||||
String _messageSearchText = "";
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -85,8 +84,6 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_messageSearchController.dispose();
|
|
||||||
_messageSearchFocusNode.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,18 +105,12 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
leading: null,
|
leading: null,
|
||||||
title:
|
title: TabBar(
|
||||||
_isSearchingMessages
|
|
||||||
? _messageSearchField()
|
|
||||||
: TabBar(
|
|
||||||
tabs:
|
tabs:
|
||||||
[SCAppLocalizations.of(context)!.message]
|
[SCAppLocalizations.of(context)!.message]
|
||||||
.map(
|
.map(
|
||||||
(e) => Padding(
|
(e) => Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(bottom: 3.w, top: 4.w),
|
||||||
bottom: 3.w,
|
|
||||||
top: 4.w,
|
|
||||||
),
|
|
||||||
child: Text(e),
|
child: Text(e),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -145,22 +136,15 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
actions: [
|
actions: [
|
||||||
SCDebounceWidget(
|
SCDebounceWidget(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsetsDirectional.only(start: 10.w, end: 10.w),
|
padding: EdgeInsetsDirectional.only(end: 10.w),
|
||||||
child:
|
child: Image.asset(
|
||||||
_isSearchingMessages
|
|
||||||
? Icon(Icons.close, size: 22.w, color: Colors.white)
|
|
||||||
: Image.asset(
|
|
||||||
"sc_images/index/sc_icon_serach.png",
|
"sc_images/index/sc_icon_serach.png",
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
height: 20.w,
|
height: 20.w,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (_isSearchingMessages) {
|
SCNavigatorUtils.push(context, SCMainRoute.mainSearch);
|
||||||
_stopMessageSearch();
|
|
||||||
} else {
|
|
||||||
_startMessageSearch();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -181,7 +165,7 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (!_isSearchingMessages) _headMenu(provider),
|
_headMenu(provider),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: EdgeInsets.only(
|
margin: EdgeInsets.only(
|
||||||
@ -189,10 +173,7 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
left: 8.w,
|
left: 8.w,
|
||||||
right: 8.w,
|
right: 8.w,
|
||||||
),
|
),
|
||||||
child: MessageConversationListPage(
|
child: MessageConversationListPage(false),
|
||||||
false,
|
|
||||||
searchQuery: _messageSearchText,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -208,76 +189,9 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _messageSearchField() {
|
|
||||||
return Container(
|
|
||||||
height: 36.w,
|
|
||||||
alignment: AlignmentDirectional.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xff18F2B1).withValues(alpha: 0.1),
|
|
||||||
borderRadius: BorderRadius.circular(20.w),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
SizedBox(width: 10.w),
|
|
||||||
Image.asset(
|
|
||||||
"sc_images/index/sc_icon_serach2.png",
|
|
||||||
width: 18.w,
|
|
||||||
height: 18.w,
|
|
||||||
color: const Color(0xff18F2B1),
|
|
||||||
),
|
|
||||||
SizedBox(width: 6.w),
|
|
||||||
Expanded(
|
|
||||||
child: TextField(
|
|
||||||
controller: _messageSearchController,
|
|
||||||
focusNode: _messageSearchFocusNode,
|
|
||||||
autofocus: true,
|
|
||||||
cursorColor: SocialChatTheme.primaryLight,
|
|
||||||
style: TextStyle(color: Colors.white, fontSize: 14.sp),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText:
|
|
||||||
"${SCAppLocalizations.of(context)!.search} ID/${SCAppLocalizations.of(context)!.userName}",
|
|
||||||
hintStyle: TextStyle(color: Colors.white54, fontSize: 13.sp),
|
|
||||||
border: InputBorder.none,
|
|
||||||
isDense: true,
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_messageSearchText = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startMessageSearch() {
|
|
||||||
setState(() {
|
|
||||||
_isSearchingMessages = true;
|
|
||||||
});
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (mounted) {
|
|
||||||
_messageSearchFocusNode.requestFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stopMessageSearch() {
|
|
||||||
_messageSearchController.clear();
|
|
||||||
_messageSearchFocusNode.unfocus();
|
|
||||||
setState(() {
|
|
||||||
_isSearchingMessages = false;
|
|
||||||
_messageSearchText = "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _headMenu(RtmProvider provider) {
|
Widget _headMenu(RtmProvider provider) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsetsDirectional.symmetric(
|
margin: EdgeInsetsDirectional.symmetric(horizontal: 25.w,).copyWith(top: 10.w),
|
||||||
horizontal: 20.w,
|
|
||||||
).copyWith(top: 10.w),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 15.w,
|
spacing: 15.w,
|
||||||
children: [
|
children: [
|
||||||
@ -351,7 +265,9 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SCDebounceWidget(
|
SCDebounceWidget(
|
||||||
child: Row(
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
Image.asset(
|
Image.asset(
|
||||||
@ -359,9 +275,8 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
width: 45.w,
|
width: 45.w,
|
||||||
height: 45.w,
|
height: 45.w,
|
||||||
),
|
),
|
||||||
SizedBox(width: 6.w),
|
SizedBox(width: 5.w),
|
||||||
Expanded(
|
Column(
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
text(
|
text(
|
||||||
@ -371,21 +286,41 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
Row(
|
text(
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: text(
|
|
||||||
"[${SCAppLocalizations.of(context)!.newMessage}]",
|
"[${SCAppLocalizations.of(context)!.newMessage}]",
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
textColor: Colors.white54,
|
textColor: Colors.white54,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
SizedBox(width: 5.w),
|
|
||||||
_unreadBadge(provider.systemUnReadCount),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
bottom: 0,
|
||||||
|
end: 10.w,
|
||||||
|
child: Visibility(
|
||||||
|
visible: provider.systemUnReadCount > 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 6.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 16.w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
//gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]),
|
||||||
|
color: Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
//borderRadius: BorderRadius.all(Radius.circular(7.w)),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"${provider.systemUnReadCount > 99 ? '99+' : provider.systemUnReadCount}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10.sp,
|
||||||
|
color: Color(0xffffffff),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -402,6 +337,10 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SCDebounceWidget(
|
SCDebounceWidget(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
@ -410,9 +349,8 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
width: 45.w,
|
width: 45.w,
|
||||||
height: 45.w,
|
height: 45.w,
|
||||||
),
|
),
|
||||||
SizedBox(width: 6.w),
|
SizedBox(width: 5.w),
|
||||||
Expanded(
|
Column(
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
text(
|
text(
|
||||||
@ -422,20 +360,41 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
Row(
|
text(
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: text(
|
|
||||||
"[${SCAppLocalizations.of(context)!.newMessage}]",
|
"[${SCAppLocalizations.of(context)!.newMessage}]",
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
textColor: Colors.white54,
|
textColor: Colors.white54,
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
SizedBox(width: 5.w),
|
)
|
||||||
_unreadBadge(provider.notifcationUnReadCount),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
bottom: 0,
|
||||||
|
end: 10.w,
|
||||||
|
child: Visibility(
|
||||||
|
visible: provider.notifcationUnReadCount > 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 6.w),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 16.w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
//gradient: LinearGradient(colors: [Color(0xffA447FF), Color(0xff623CE9)]),
|
||||||
|
color: Colors.red,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
//borderRadius: BorderRadius.all(Radius.circular(7.w)),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"${provider.notifcationUnReadCount > 99 ? '99+' : provider.notifcationUnReadCount}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10.sp,
|
||||||
|
color: Color(0xffffffff),
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -453,25 +412,4 @@ class _MessagePageState extends State<SCMessagePage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _unreadBadge(int unreadCount) {
|
|
||||||
return Visibility(
|
|
||||||
visible: unreadCount > 0,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 6.w),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
height: 16.w,
|
|
||||||
decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle),
|
|
||||||
child: Text(
|
|
||||||
"${unreadCount > 99 ? '99+' : unreadCount}",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10.sp,
|
|
||||||
color: Color(0xffffffff),
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,928 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_cp_rights_config_res.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_room_effect_scheduler.dart';
|
|
||||||
|
|
||||||
enum _CpGuideTab { note, rights }
|
|
||||||
|
|
||||||
enum _CpRightsCategory { cp, brother, sisters }
|
|
||||||
|
|
||||||
class CpRightsGuidePage extends StatefulWidget {
|
|
||||||
const CpRightsGuidePage({super.key, this.cpUserId});
|
|
||||||
|
|
||||||
final String? cpUserId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CpRightsGuidePage> createState() => _CpRightsGuidePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CpRightsGuidePageState extends State<CpRightsGuidePage> {
|
|
||||||
static const String _assetBase = "sc_images/room/cp_rights";
|
|
||||||
static const Color _textColor = Color(0xff543356);
|
|
||||||
static const Color _accentColor = Color(0xffff2a70);
|
|
||||||
static const String _effectSuppressReason = "cp_rights_guide_page";
|
|
||||||
static const String _roomRocketPagLaunchDialogTag =
|
|
||||||
"showRoomRocketPagLaunchDialog";
|
|
||||||
|
|
||||||
_CpGuideTab _tab = _CpGuideTab.note;
|
|
||||||
_CpRightsCategory _rightsCategory = _CpRightsCategory.cp;
|
|
||||||
RtcProvider? _suppressedRtcProvider;
|
|
||||||
bool _roomEffectsSuppressed = false;
|
|
||||||
bool _restoreRoomVisualEffects = false;
|
|
||||||
final Map<_CpRightsCategory, List<_CpRightsLevel>> _rightsRowsByCategory = {};
|
|
||||||
final Set<_CpRightsCategory> _loadingRightsCategories = {};
|
|
||||||
final Set<_CpRightsCategory> _loadedRightsCategories = {};
|
|
||||||
final Set<_CpRightsCategory> _failedRightsCategories = {};
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_suppressRoomEffects();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_releaseRoomEffects();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xffffe7eb),
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(child: _buildBackground()),
|
|
||||||
SafeArea(
|
|
||||||
top: true,
|
|
||||||
bottom: false,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
_buildNavigationBar(),
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
padding: EdgeInsets.only(bottom: 28.w),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(height: 15.w),
|
|
||||||
_buildTopTabs(),
|
|
||||||
SizedBox(height: 14.w),
|
|
||||||
if (_tab == _CpGuideTab.note)
|
|
||||||
_buildNoteContent()
|
|
||||||
else
|
|
||||||
_buildRightsContent(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBackground() {
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topCenter,
|
|
||||||
end: Alignment.bottomCenter,
|
|
||||||
colors: [Color(0xffffd8e6), Color(0xffffe7eb)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: 0.6,
|
|
||||||
child: Image.asset(
|
|
||||||
"$_assetBase/sc_cp_rights_bg.png",
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildNavigationBar() {
|
|
||||||
return SizedBox(
|
|
||||||
height: 44.w,
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
PositionedDirectional(
|
|
||||||
start: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => Navigator.of(context).maybePop(),
|
|
||||||
child: SizedBox(
|
|
||||||
width: 44.w,
|
|
||||||
child: Center(
|
|
||||||
child: Transform.flip(
|
|
||||||
flipX: SCGlobalConfig.lang == "ar",
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_back_ios_new,
|
|
||||||
color: Colors.black,
|
|
||||||
size: 18.w,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 54.w),
|
|
||||||
child: Text(
|
|
||||||
"Gifts For Close Friends",
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 18.sp,
|
|
||||||
height: 1,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildTopTabs() {
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
_buildMainTab(label: "Note", tab: _CpGuideTab.note),
|
|
||||||
SizedBox(width: 44.w),
|
|
||||||
_buildMainTab(label: "Rights", tab: _CpGuideTab.rights),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMainTab({required String label, required _CpGuideTab tab}) {
|
|
||||||
final selected = _tab == tab;
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
if (_tab == tab) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() => _tab = tab);
|
|
||||||
if (tab == _CpGuideTab.rights) {
|
|
||||||
_ensureRightsConfigLoaded(_rightsCategory);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: SizedBox(
|
|
||||||
width: 116.w,
|
|
||||||
height: 36.w,
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.asset(
|
|
||||||
selected
|
|
||||||
? "$_assetBase/sc_cp_rights_tab_selected.png"
|
|
||||||
: "$_assetBase/sc_cp_rights_tab_unselected.png",
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: TextStyle(
|
|
||||||
color: selected ? Colors.white : _textColor,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
height: 1,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildNoteContent() {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
_CpOrnatePanel(
|
|
||||||
assetBase: _assetBase,
|
|
||||||
title: "What is a close friendship?",
|
|
||||||
height: 223.w,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(20.w, 67.w, 20.w, 18.w),
|
|
||||||
child: Text(
|
|
||||||
"The \"Best Friends\" feature is a brand-new interactive way to celebrate close relationships between users. By sending gifts to someone and requesting to become best friends, you can form a best friend connection once they accept.\n"
|
|
||||||
"'CP' connections can only be formed between a man and a woman, while \"brother\" and \"sister\" connections have no gender restrictions.",
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12.w),
|
|
||||||
_CpOrnatePanel(
|
|
||||||
assetBase: _assetBase,
|
|
||||||
title: "How to Form Relationships?",
|
|
||||||
height: 940.w,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(20.w, 67.w, 20.w, 16.w),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_bodyText(
|
|
||||||
"Request for a Gift Panel in a Group Chat Room",
|
|
||||||
color: _accentColor,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
SizedBox(height: 5.w),
|
|
||||||
_bodyText(
|
|
||||||
"1. In the room, tap [Gift Panel] > select [Best Friends];",
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
_noteImage("sc_cp_rights_note_gift_panel.png", 122),
|
|
||||||
SizedBox(height: 13.w),
|
|
||||||
_bodyText(
|
|
||||||
"2. Choose a gift tailored to a specific type of close friend; once you send it, a friend request will be automatically sent;",
|
|
||||||
),
|
|
||||||
SizedBox(height: 10.w),
|
|
||||||
_noteImage("sc_cp_rights_note_relation_types.png", 42),
|
|
||||||
SizedBox(height: 13.w),
|
|
||||||
_bodyText(
|
|
||||||
"3. The other party must accept the match within 24 hours;",
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
_noteImage("sc_cp_rights_note_invitation.png", 408),
|
|
||||||
SizedBox(height: 9.w),
|
|
||||||
_bodyText(
|
|
||||||
"*If the other party does not respond within 24 hours of the request being sent, the connection will be automatically declined.",
|
|
||||||
color: _accentColor,
|
|
||||||
fontSize: 10,
|
|
||||||
lineHeight: 1.4,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12.w),
|
|
||||||
_CpOrnatePanel(
|
|
||||||
assetBase: _assetBase,
|
|
||||||
title: "Intimacy Level",
|
|
||||||
height: 174.w,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(20.w, 67.w, 20.w, 16.w),
|
|
||||||
child: _bodyText(
|
|
||||||
"Intimacy Points are a numerical representation of the level of closeness between two players. 1 Gold Coin equals 1 Intimacy Point, and you can increase your Intimacy Points by sending gifts to your best friend every day.",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12.w),
|
|
||||||
_CpOrnatePanel(
|
|
||||||
assetBase: _assetBase,
|
|
||||||
title: "FAQ",
|
|
||||||
height: 486.w,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(20.w, 67.w, 20.w, 18.w),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: const [
|
|
||||||
_FaqItem(
|
|
||||||
question:
|
|
||||||
"Q1: Does giving someone a regular gift increase intimacy?",
|
|
||||||
answer: "No increase.",
|
|
||||||
),
|
|
||||||
_FaqItem(
|
|
||||||
question:
|
|
||||||
"Q2: Is there a limit to the number of relationships?",
|
|
||||||
answer:
|
|
||||||
"You can only have one CP, and a maximum of three brothers or sisters. More slots will be added in the future.",
|
|
||||||
),
|
|
||||||
_FaqItem(
|
|
||||||
question: "Q3: Who can I invite to be a CP?",
|
|
||||||
answer:
|
|
||||||
"As long as neither party has a CP, you can send a CP gift to extend a relationship invitation.",
|
|
||||||
),
|
|
||||||
_FaqItem(
|
|
||||||
question:
|
|
||||||
"Q4: If my application is denied, will the Best Friend Gift be refunded?",
|
|
||||||
answer:
|
|
||||||
"Experience points are awarded the moment the gift is sent. If the request expires or is rejected, the gift will not be returned.",
|
|
||||||
),
|
|
||||||
_FaqItem(
|
|
||||||
question:
|
|
||||||
"Q5: Can the termination of the relationship be reversed?",
|
|
||||||
answer:
|
|
||||||
"Once the relationship is dissolved, all rights and interests will be reclaimed by the platform. The action cannot be undone; the relationship can only be reestablished.",
|
|
||||||
isLast: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _noteImage(String name, double height) {
|
|
||||||
return ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
|
||||||
child: Image.asset(
|
|
||||||
"$_assetBase/$name",
|
|
||||||
width: 310.w,
|
|
||||||
height: height.w,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextStyle _bodyStyle({
|
|
||||||
Color color = _textColor,
|
|
||||||
FontWeight fontWeight = FontWeight.w400,
|
|
||||||
double fontSize = 12,
|
|
||||||
double lineHeight = 1.333,
|
|
||||||
}) {
|
|
||||||
return TextStyle(
|
|
||||||
color: color,
|
|
||||||
fontSize: fontSize.sp,
|
|
||||||
height: lineHeight,
|
|
||||||
fontWeight: fontWeight,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _bodyText(
|
|
||||||
String value, {
|
|
||||||
Color color = _textColor,
|
|
||||||
FontWeight fontWeight = FontWeight.w400,
|
|
||||||
double fontSize = 12,
|
|
||||||
double lineHeight = 1.333,
|
|
||||||
}) {
|
|
||||||
return Text(
|
|
||||||
value,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(
|
|
||||||
color: color,
|
|
||||||
fontWeight: fontWeight,
|
|
||||||
fontSize: fontSize,
|
|
||||||
lineHeight: lineHeight,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightsContent() {
|
|
||||||
final rows = _currentRightsRowsFor(_rightsCategory);
|
|
||||||
final isInitialLoading =
|
|
||||||
_loadingRightsCategories.contains(_rightsCategory) &&
|
|
||||||
!_rightsRowsByCategory.containsKey(_rightsCategory);
|
|
||||||
final hasLoadError =
|
|
||||||
_failedRightsCategories.contains(_rightsCategory) &&
|
|
||||||
!_rightsRowsByCategory.containsKey(_rightsCategory);
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 52.w),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_buildRightsCategoryTab("CP", _CpRightsCategory.cp),
|
|
||||||
_buildRightsCategoryTab("Brother", _CpRightsCategory.brother),
|
|
||||||
_buildRightsCategoryTab("Sisters", _CpRightsCategory.sisters),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 13.w),
|
|
||||||
if (hasLoadError)
|
|
||||||
_buildRightsLoadError(_rightsCategory)
|
|
||||||
else
|
|
||||||
Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
_buildRightsTable(rows),
|
|
||||||
if (isInitialLoading)
|
|
||||||
SizedBox(
|
|
||||||
width: 22.w,
|
|
||||||
height: 22.w,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2.w,
|
|
||||||
color: _accentColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightsCategoryTab(String label, _CpRightsCategory category) {
|
|
||||||
final selected = _rightsCategory == category;
|
|
||||||
final textStyle = TextStyle(
|
|
||||||
color: selected ? _accentColor : _textColor.withValues(alpha: 0.5),
|
|
||||||
fontSize: 16.sp,
|
|
||||||
height: 1,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
);
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () {
|
|
||||||
if (_rightsCategory == category) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() => _rightsCategory = category);
|
|
||||||
_ensureRightsConfigLoaded(category);
|
|
||||||
},
|
|
||||||
child: SizedBox(
|
|
||||||
height: 26.w,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(label, textScaler: TextScaler.noScaling, style: textStyle),
|
|
||||||
SizedBox(height: 6.w),
|
|
||||||
if (selected)
|
|
||||||
Container(
|
|
||||||
width: _rightsIndicatorWidth(label, textStyle),
|
|
||||||
height: 3.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: _accentColor,
|
|
||||||
borderRadius: BorderRadius.circular(31.5.w),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightsLoadError(_CpRightsCategory category) {
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => _ensureRightsConfigLoaded(category, force: true),
|
|
||||||
child: Container(
|
|
||||||
width: 351.w,
|
|
||||||
height: 156.w,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withValues(alpha: 0.3),
|
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
|
||||||
),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
|
||||||
child: Text(
|
|
||||||
"Failed to load rights. Tap to retry.",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(color: _accentColor, lineHeight: 1.3),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_CpRightsLevel> _rightsRowsFor(_CpRightsCategory category) {
|
|
||||||
switch (category) {
|
|
||||||
case _CpRightsCategory.cp:
|
|
||||||
return const [
|
|
||||||
_CpRightsLevel(level: "Lv.1", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.2", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.3", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.4", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.5", intimacy: "--"),
|
|
||||||
];
|
|
||||||
case _CpRightsCategory.brother:
|
|
||||||
return const [
|
|
||||||
_CpRightsLevel(level: "Lv.1", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.2", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.3", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.4", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.5", intimacy: "--"),
|
|
||||||
];
|
|
||||||
case _CpRightsCategory.sisters:
|
|
||||||
return const [
|
|
||||||
_CpRightsLevel(level: "Lv.1", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.2", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.3", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.4", intimacy: "--"),
|
|
||||||
_CpRightsLevel(level: "Lv.5", intimacy: "--"),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_CpRightsLevel> _currentRightsRowsFor(_CpRightsCategory category) {
|
|
||||||
return _rightsRowsByCategory[category] ?? _rightsRowsFor(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _ensureRightsConfigLoaded(
|
|
||||||
_CpRightsCategory category, {
|
|
||||||
bool force = false,
|
|
||||||
}) async {
|
|
||||||
if (!force &&
|
|
||||||
(_loadedRightsCategories.contains(category) ||
|
|
||||||
_loadingRightsCategories.contains(category))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final relationType = _relationTypeForRightsCategory(category);
|
|
||||||
setState(() {
|
|
||||||
_loadingRightsCategories.add(category);
|
|
||||||
_failedRightsCategories.remove(category);
|
|
||||||
});
|
|
||||||
_rightsDebugLog(
|
|
||||||
'load start relationType=$relationType cpUserId=${widget.cpUserId}',
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
final config = await SCAccountRepository().cpRightsConfig(
|
|
||||||
relationType: relationType,
|
|
||||||
cpUserId: widget.cpUserId,
|
|
||||||
);
|
|
||||||
final rows = _rightsRowsFromConfig(config, category);
|
|
||||||
if (rows.isEmpty) {
|
|
||||||
throw StateError('empty CP rights levels');
|
|
||||||
}
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_rightsRowsByCategory[category] = rows;
|
|
||||||
_loadedRightsCategories.add(category);
|
|
||||||
_failedRightsCategories.remove(category);
|
|
||||||
_loadingRightsCategories.remove(category);
|
|
||||||
});
|
|
||||||
_rightsDebugLog(
|
|
||||||
'load success relationType=$relationType levels=${rows.length}',
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_loadingRightsCategories.remove(category);
|
|
||||||
_failedRightsCategories.add(category);
|
|
||||||
});
|
|
||||||
_rightsDebugLog('load failed relationType=$relationType error=$error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_CpRightsLevel> _rightsRowsFromConfig(
|
|
||||||
SCCpRightsConfigRes config,
|
|
||||||
_CpRightsCategory category,
|
|
||||||
) {
|
|
||||||
final rows = <_CpRightsLevel>[];
|
|
||||||
for (final level in config.levels) {
|
|
||||||
final levelNumber = level.level;
|
|
||||||
final levelName = level.levelName?.trim();
|
|
||||||
final requiredValue = level.requiredIntimacyValue ?? 0;
|
|
||||||
rows.add(
|
|
||||||
_CpRightsLevel(
|
|
||||||
level:
|
|
||||||
levelName?.isNotEmpty == true
|
|
||||||
? levelName!
|
|
||||||
: (levelNumber != null
|
|
||||||
? "Lv.$levelNumber"
|
|
||||||
: "Lv.${rows.length + 1}"),
|
|
||||||
intimacy: _formatRightsIntimacyValue(requiredValue),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _relationTypeForRightsCategory(_CpRightsCategory category) {
|
|
||||||
switch (category) {
|
|
||||||
case _CpRightsCategory.cp:
|
|
||||||
return "CP";
|
|
||||||
case _CpRightsCategory.brother:
|
|
||||||
return "BROTHER";
|
|
||||||
case _CpRightsCategory.sisters:
|
|
||||||
return "SISTERS";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatRightsIntimacyValue(int value) {
|
|
||||||
final text = value.abs().toString();
|
|
||||||
final buffer = StringBuffer();
|
|
||||||
for (var index = 0; index < text.length; index++) {
|
|
||||||
if (index > 0 && (text.length - index) % 3 == 0) {
|
|
||||||
buffer.write(",");
|
|
||||||
}
|
|
||||||
buffer.write(text[index]);
|
|
||||||
}
|
|
||||||
return value < 0 ? "-$buffer" : buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rightsDebugLog(String message) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint("[CP][Rights] $message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightsTable(List<_CpRightsLevel> rows) {
|
|
||||||
const tableWidth = 351.0;
|
|
||||||
const columnWidth = tableWidth / 2;
|
|
||||||
const headerHeight = 39.0;
|
|
||||||
const rowHeight = 77.0;
|
|
||||||
final tableHeight = headerHeight + rows.length * rowHeight;
|
|
||||||
|
|
||||||
return ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
|
||||||
child: Container(
|
|
||||||
width: tableWidth.w,
|
|
||||||
height: tableHeight.w,
|
|
||||||
color: Colors.white.withValues(alpha: 0.3),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
left: columnWidth.w,
|
|
||||||
width: columnWidth.w,
|
|
||||||
child: ColoredBox(color: Colors.white.withValues(alpha: 0.24)),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: headerHeight.w,
|
|
||||||
child: _tableLine(width: tableWidth),
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: headerHeight.w,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
_tableHeader("Level", columnWidth),
|
|
||||||
_tableHeader("Intimacy Level", columnWidth),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (final row in rows) _buildRightsLevelRow(row),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _tableHeader(String value, double width) {
|
|
||||||
return SizedBox(
|
|
||||||
width: width.w,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
value,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(fontSize: 12, lineHeight: 1.2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRightsLevelRow(_CpRightsLevel row) {
|
|
||||||
const tableWidth = 351.0;
|
|
||||||
const columnWidth = tableWidth / 2;
|
|
||||||
const rowHeight = 77.0;
|
|
||||||
return SizedBox(
|
|
||||||
height: rowHeight.w,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
_spanCell(row.level, width: columnWidth, height: rowHeight),
|
|
||||||
_spanCell(row.intimacy, width: columnWidth, height: rowHeight),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: _tableLine(width: tableWidth),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _spanCell(
|
|
||||||
String value, {
|
|
||||||
required double width,
|
|
||||||
required double height,
|
|
||||||
}) {
|
|
||||||
return SizedBox(
|
|
||||||
width: width.w,
|
|
||||||
height: height.w,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
value,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: _bodyStyle(fontSize: 12, lineHeight: 1.2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _tableLine({required double width}) {
|
|
||||||
return Container(width: width.w, height: 1.w, color: Colors.white);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _rightsIndicatorWidth(String label, TextStyle style) {
|
|
||||||
final painter = TextPainter(
|
|
||||||
text: TextSpan(text: label, style: style),
|
|
||||||
textDirection: Directionality.of(context),
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
maxLines: 1,
|
|
||||||
)..layout();
|
|
||||||
return painter.width.clamp(16.w, 72.w);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _suppressRoomEffects() {
|
|
||||||
if (_roomEffectsSuppressed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomEffectsSuppressed = true;
|
|
||||||
try {
|
|
||||||
final rtcProvider = context.read<RtcProvider>();
|
|
||||||
_suppressedRtcProvider = rtcProvider;
|
|
||||||
_restoreRoomVisualEffects = rtcProvider.roomVisualEffectsEnabled;
|
|
||||||
rtcProvider.setRoomVisualEffectsEnabled(false);
|
|
||||||
} catch (_) {}
|
|
||||||
OverlayManager().beginSuppressFloatingScreens(
|
|
||||||
reason: _effectSuppressReason,
|
|
||||||
);
|
|
||||||
SCRoomEffectScheduler().clearDeferredTasks(reason: _effectSuppressReason);
|
|
||||||
SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag);
|
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
|
||||||
SCGiftVapSvgaManager().pauseAnim();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _releaseRoomEffects() {
|
|
||||||
if (!_roomEffectsSuppressed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomEffectsSuppressed = false;
|
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
|
||||||
OverlayManager().endSuppressFloatingScreens(reason: _effectSuppressReason);
|
|
||||||
final rtcProvider = _suppressedRtcProvider;
|
|
||||||
if (rtcProvider != null && _restoreRoomVisualEffects) {
|
|
||||||
rtcProvider.setRoomVisualEffectsEnabled(true);
|
|
||||||
}
|
|
||||||
SCGiftVapSvgaManager().resumeAnim();
|
|
||||||
_suppressedRtcProvider = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CpOrnatePanel extends StatelessWidget {
|
|
||||||
const _CpOrnatePanel({
|
|
||||||
required this.assetBase,
|
|
||||||
required this.title,
|
|
||||||
required this.height,
|
|
||||||
required this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
static const Color _textColor = Color(0xff543356);
|
|
||||||
|
|
||||||
final String assetBase;
|
|
||||||
final String title;
|
|
||||||
final double height;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final middleHeight = (height - 65.w - 73.w).clamp(1.0, double.infinity);
|
|
||||||
return SizedBox(
|
|
||||||
width: 350.w,
|
|
||||||
height: height,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Image.asset(
|
|
||||||
"$assetBase/sc_cp_rights_panel_top.png",
|
|
||||||
width: 350.w,
|
|
||||||
height: 65.w,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
Image.asset(
|
|
||||||
"$assetBase/sc_cp_rights_panel_mid.png",
|
|
||||||
width: 350.w,
|
|
||||||
height: middleHeight,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
Image.asset(
|
|
||||||
"$assetBase/sc_cp_rights_panel_bottom.png",
|
|
||||||
width: 350.w,
|
|
||||||
height: 73.w,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: 24.w,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: Text(
|
|
||||||
title,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: TextStyle(
|
|
||||||
color: _textColor,
|
|
||||||
fontSize: 16.sp,
|
|
||||||
height: 1,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
left: 30.w,
|
|
||||||
right: 30.w,
|
|
||||||
top: 44.w,
|
|
||||||
child: Image.asset(
|
|
||||||
"$assetBase/sc_cp_rights_panel_divider.png",
|
|
||||||
height: 20.w,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned.fill(child: child),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FaqItem extends StatelessWidget {
|
|
||||||
const _FaqItem({
|
|
||||||
required this.question,
|
|
||||||
required this.answer,
|
|
||||||
this.isLast = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
static const Color _textColor = Color(0xff543356);
|
|
||||||
static const Color _accentColor = Color(0xffff2a70);
|
|
||||||
|
|
||||||
final String question;
|
|
||||||
final String answer;
|
|
||||||
final bool isLast;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: isLast ? 0 : 8.w),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
question,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: TextStyle(
|
|
||||||
color: _accentColor,
|
|
||||||
fontSize: 12.sp,
|
|
||||||
height: 1.333,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 2.w),
|
|
||||||
Text(
|
|
||||||
answer,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textScaler: TextScaler.noScaling,
|
|
||||||
style: TextStyle(
|
|
||||||
color: _textColor,
|
|
||||||
fontSize: 12.sp,
|
|
||||||
height: 1.333,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
decoration: TextDecoration.none,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CpRightsLevel {
|
|
||||||
const _CpRightsLevel({required this.level, required this.intimacy});
|
|
||||||
|
|
||||||
final String level;
|
|
||||||
final String intimacy;
|
|
||||||
}
|
|
||||||
@ -20,11 +20,11 @@ import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart';
|
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
|
||||||
import 'package:yumi/main.dart';
|
import 'package:yumi/main.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/tools/sc_dialog_utils.dart';
|
import 'package:yumi/shared/tools/sc_dialog_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||||
@ -33,12 +33,12 @@ import 'package:yumi/services/general/sc_app_general_manager.dart';
|
|||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
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/gift/sc_gift_combo_send_button.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/gift/gift_tab_page.dart';
|
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
||||||
import 'package:yumi/modules/gift/cp_rights/cp_rights_guide_page.dart';
|
|
||||||
import 'package:yumi/services/gift/room_gift_combo_send_controller.dart';
|
import 'package:yumi/services/gift/room_gift_combo_send_controller.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_recharge_bottom_sheet.dart';
|
|
||||||
import '../../shared/data_sources/models/enum/sc_gift_type.dart';
|
import '../../shared/data_sources/models/enum/sc_gift_type.dart';
|
||||||
import '../../shared/data_sources/models/message/sc_floating_message.dart';
|
import '../../shared/data_sources/models/message/sc_floating_message.dart';
|
||||||
import '../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
import '../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
||||||
@ -52,18 +52,14 @@ class _GiftPageTabItem {
|
|||||||
|
|
||||||
class GiftPage extends StatefulWidget {
|
class GiftPage extends StatefulWidget {
|
||||||
final SocialChatUserProfile? toUser;
|
final SocialChatUserProfile? toUser;
|
||||||
final String? initialTabType;
|
|
||||||
|
|
||||||
const GiftPage({super.key, this.toUser, this.initialTabType});
|
const GiftPage({super.key, this.toUser});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GiftPage> createState() => _GiftPageState();
|
State<GiftPage> createState() => _GiftPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
||||||
static const String _cpProfileAssetBase = "sc_images/room/cp_profile";
|
|
||||||
static const String _firstRechargeGiftEntryCard =
|
|
||||||
"sc_images/first_recharge/room_gift_entry_card.png";
|
|
||||||
static const String _backpackGiftTab = SCAppGeneralManager.backpackGiftTab;
|
static const String _backpackGiftTab = SCAppGeneralManager.backpackGiftTab;
|
||||||
static const Duration _comboSendBatchWindow = Duration(milliseconds: 200);
|
static const Duration _comboSendBatchWindow = Duration(milliseconds: 200);
|
||||||
|
|
||||||
@ -79,7 +75,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
TabController? _tabController;
|
TabController? _tabController;
|
||||||
List<String> _tabTypes = <String>[];
|
List<String> _tabTypes = <String>[];
|
||||||
bool _didApplyInitialTab = false;
|
|
||||||
|
|
||||||
/// 业务逻辑策略访问器
|
/// 业务逻辑策略访问器
|
||||||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||||||
@ -208,9 +203,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
number = 1;
|
number = 1;
|
||||||
noShowNumber = false;
|
noShowNumber = false;
|
||||||
giftType = _giftTypeFromTab(gift?.giftTab);
|
giftType = _giftTypeFromTab(gift?.giftTab);
|
||||||
if (_isCpGift(gift)) {
|
|
||||||
_normalizeCpRecipientSelection();
|
|
||||||
}
|
|
||||||
if (notify) {
|
if (notify) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
@ -228,7 +220,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
Provider.of<SCAppGeneralManager>(
|
Provider.of<SCAppGeneralManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).giftList(includeCustomized: true);
|
).giftList(includeCustomized: false);
|
||||||
Provider.of<SCAppGeneralManager>(context, listen: false).giftActivityList();
|
Provider.of<SCAppGeneralManager>(context, listen: false).giftActivityList();
|
||||||
unawaited(
|
unawaited(
|
||||||
Provider.of<SCAppGeneralManager>(
|
Provider.of<SCAppGeneralManager>(
|
||||||
@ -268,53 +260,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
final ref = Provider.of<SCAppGeneralManager>(context, listen: false);
|
final ref = Provider.of<SCAppGeneralManager>(context, listen: false);
|
||||||
_syncSelectedGiftForTab(ref, _tabTypes[controller.index]);
|
_syncSelectedGiftForTab(ref, _tabTypes[controller.index]);
|
||||||
_normalizeCpRecipientSelection(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isCurrentCpGiftTab {
|
|
||||||
final controller = _tabController;
|
|
||||||
if (controller == null || controller.index >= _tabTypes.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return _isCpGiftTab(_tabTypes[controller.index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isCpGiftTab(String tabType) => scIsCpGiftTab(tabType);
|
|
||||||
|
|
||||||
bool _isCpGift(SocialChatGiftRes? gift) => scIsCpGift(gift);
|
|
||||||
|
|
||||||
bool get _isCpRecipientSingleMode =>
|
|
||||||
_isCurrentCpGiftTab || _isCpGift(checkedGift);
|
|
||||||
|
|
||||||
void _normalizeCpRecipientSelection({bool notify = false}) {
|
|
||||||
if (widget.toUser != null ||
|
|
||||||
(!_isCurrentCpGiftTab && !_isCpGift(checkedGift))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var changed = false;
|
|
||||||
var keptSelected = false;
|
|
||||||
for (final mai in listMai) {
|
|
||||||
if (!mai.isSelect) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!keptSelected) {
|
|
||||||
keptSelected = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mai.isSelect = false;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (isAll) {
|
|
||||||
isAll = false;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (giveType == 2) {
|
|
||||||
giveType = 1;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
if (notify && changed && mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<_GiftPageTabItem> _buildGiftTabs(
|
List<_GiftPageTabItem> _buildGiftTabs(
|
||||||
@ -325,6 +270,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
final availableTypes =
|
final availableTypes =
|
||||||
ref.giftByTab.entries
|
ref.giftByTab.entries
|
||||||
.where((entry) => entry.value.isNotEmpty)
|
.where((entry) => entry.value.isNotEmpty)
|
||||||
|
.where((entry) => entry.key != "CUSTOMIZED")
|
||||||
.where((entry) => entry.key != _backpackGiftTab)
|
.where((entry) => entry.key != _backpackGiftTab)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toList();
|
.toList();
|
||||||
@ -366,7 +312,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
) {
|
) {
|
||||||
final nextTypes = tabs.map((tab) => tab.type).toList();
|
final nextTypes = tabs.map((tab) => tab.type).toList();
|
||||||
if (_tabController != null && listEquals(_tabTypes, nextTypes)) {
|
if (_tabController != null && listEquals(_tabTypes, nextTypes)) {
|
||||||
_applyInitialTabIfNeeded(nextTypes);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,20 +321,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
_tabController!.index < _tabTypes.length)
|
_tabController!.index < _tabTypes.length)
|
||||||
? _tabTypes[_tabController!.index]
|
? _tabTypes[_tabController!.index]
|
||||||
: null;
|
: null;
|
||||||
final requestedType = widget.initialTabType?.trim();
|
final nextIndex = currentType != null ? nextTypes.indexOf(currentType) : 0;
|
||||||
final requestedIndex =
|
|
||||||
!_didApplyInitialTab && (requestedType?.isNotEmpty ?? false)
|
|
||||||
? nextTypes.indexOf(requestedType!)
|
|
||||||
: -1;
|
|
||||||
final nextIndex =
|
|
||||||
requestedIndex >= 0
|
|
||||||
? requestedIndex
|
|
||||||
: currentType != null
|
|
||||||
? nextTypes.indexOf(currentType)
|
|
||||||
: 0;
|
|
||||||
if (requestedIndex >= 0) {
|
|
||||||
_didApplyInitialTab = true;
|
|
||||||
}
|
|
||||||
_tabController?.removeListener(_handleTabChanged);
|
_tabController?.removeListener(_handleTabChanged);
|
||||||
_tabController?.dispose();
|
_tabController?.dispose();
|
||||||
_tabTypes = nextTypes;
|
_tabTypes = nextTypes;
|
||||||
@ -405,27 +337,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _applyInitialTabIfNeeded(List<String> tabTypes) {
|
|
||||||
final requestedType = widget.initialTabType?.trim();
|
|
||||||
if (_didApplyInitialTab || requestedType == null || requestedType.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final requestedIndex = tabTypes.indexOf(requestedType);
|
|
||||||
final controller = _tabController;
|
|
||||||
if (requestedIndex < 0 || controller == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_didApplyInitialTab = true;
|
|
||||||
if (controller.index != requestedIndex) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted || _tabController != controller) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
controller.animateTo(requestedIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _syncSelectedGiftForTab(
|
void _syncSelectedGiftForTab(
|
||||||
SCAppGeneralManager ref,
|
SCAppGeneralManager ref,
|
||||||
String tabType, {
|
String tabType, {
|
||||||
@ -440,17 +351,11 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
void _ensureCurrentTabSelection(SCAppGeneralManager ref, String tabType) {
|
void _ensureCurrentTabSelection(SCAppGeneralManager ref, String tabType) {
|
||||||
final gifts = ref.giftByTab[tabType] ?? const <SocialChatGiftRes>[];
|
final gifts = ref.giftByTab[tabType] ?? const <SocialChatGiftRes>[];
|
||||||
if (_matchesCurrentGift(tabType, gifts)) {
|
if (_matchesCurrentGift(tabType, gifts)) {
|
||||||
if (_isCpGiftTab(tabType)) {
|
|
||||||
_normalizeCpRecipientSelection();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final gift = _resolveSelectedGift(tabType, gifts);
|
final gift = _resolveSelectedGift(tabType, gifts);
|
||||||
_selectedGiftByTab[tabType] = gift;
|
_selectedGiftByTab[tabType] = gift;
|
||||||
_applyGiftSelection(gift, notify: false);
|
_applyGiftSelection(gift, notify: false);
|
||||||
if (_isCpGiftTab(tabType)) {
|
|
||||||
_normalizeCpRecipientSelection();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialChatGiftRes? _resolveSelectedGift(
|
SocialChatGiftRes? _resolveSelectedGift(
|
||||||
@ -637,19 +542,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
return errorText.contains('INSUFFICIENT_BALANCE');
|
return errorText.contains('INSUFFICIENT_BALANCE');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isInsufficientMoneyError(Object error, String resolvedMessage) {
|
|
||||||
final errorText =
|
|
||||||
'${_readBackendErrorText(error)} $resolvedMessage'.toLowerCase();
|
|
||||||
return errorText.contains('no enough money') ||
|
|
||||||
errorText.contains('not enough money') ||
|
|
||||||
errorText.contains('insufficient money') ||
|
|
||||||
errorText.contains('insufficient balance') ||
|
|
||||||
errorText.contains('insufficient_balance') ||
|
|
||||||
errorText.contains('balance not enough') ||
|
|
||||||
errorText.contains('not enough gold') ||
|
|
||||||
errorText.contains('insufficient gold');
|
|
||||||
}
|
|
||||||
|
|
||||||
String _readBackendErrorText(Object error) {
|
String _readBackendErrorText(Object error) {
|
||||||
final values = <String>[];
|
final values = <String>[];
|
||||||
void addValue(dynamic value) {
|
void addValue(dynamic value) {
|
||||||
@ -692,34 +584,89 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
if (tabController == null) {
|
if (tabController == null) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
final currentTabType = giftTabs[tabController.index].type;
|
|
||||||
final isCpTab = currentTabType == "CP";
|
|
||||||
final showFirstRechargeEntry =
|
|
||||||
!SCGlobalConfig.isReview &&
|
|
||||||
!isCpTab &&
|
|
||||||
ref.shouldShowFirstRechargeReward;
|
|
||||||
_ensureCurrentTabSelection(ref, giftTabs[tabController.index].type);
|
_ensureCurrentTabSelection(ref, giftTabs[tabController.index].type);
|
||||||
final isCpRecipientSingleMode = isCpTab || _isCpGift(checkedGift);
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
SCGlobalConfig.isReview
|
||||||
|
? Container()
|
||||||
|
: ((AccountStorage()
|
||||||
|
.getCurrentUser()
|
||||||
|
?.userProfile
|
||||||
|
?.firstRecharge ??
|
||||||
|
false)
|
||||||
|
? GestureDetector(
|
||||||
|
child: Stack(
|
||||||
|
alignment: AlignmentDirectional.bottomEnd,
|
||||||
|
children: [
|
||||||
|
Transform.flip(
|
||||||
|
flipX:
|
||||||
|
SCGlobalConfig.lang == "ar"
|
||||||
|
? true
|
||||||
|
: false, // 水平翻转
|
||||||
|
flipY: false, // 垂直翻转设为 false
|
||||||
|
child: Image.asset(
|
||||||
|
_strategy
|
||||||
|
.getGiftPageFirstRechargeRoomTagIcon(),
|
||||||
|
height: 75.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SCGlobalConfig.lang == "ar"
|
||||||
|
? PositionedDirectional(
|
||||||
|
end: 22.w,
|
||||||
|
bottom: 38.w,
|
||||||
|
child: Image.asset(
|
||||||
|
_strategy
|
||||||
|
.getGiftPageFirstRechargeTextIcon(
|
||||||
|
'ar',
|
||||||
|
),
|
||||||
|
height: 13.w,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: PositionedDirectional(
|
||||||
|
end: 16.w,
|
||||||
|
bottom: 38.w,
|
||||||
|
child: Image.asset(
|
||||||
|
_strategy
|
||||||
|
.getGiftPageFirstRechargeTextIcon(
|
||||||
|
'en',
|
||||||
|
),
|
||||||
|
height: 13.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
end: 34.w,
|
||||||
|
bottom: 13.w,
|
||||||
|
child: CountdownTimer(
|
||||||
|
expiryDate:
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
AccountStorage()
|
||||||
|
.getCurrentUser()
|
||||||
|
?.userProfile
|
||||||
|
?.firstRechargeEndTime ??
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 12.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
SCDialogUtils.showFirstRechargeDialog(
|
||||||
|
navigatorKey.currentState!.context,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
_buildGiftHead(),
|
_buildGiftHead(),
|
||||||
if (showFirstRechargeEntry) ...[
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
|
||||||
child: _buildFirstRechargeGiftEntry(),
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
],
|
|
||||||
if (isCpTab) ...[
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
|
||||||
child: _buildCpGiftCloseFriendEntry(),
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
],
|
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(12.0),
|
topLeft: Radius.circular(12.0),
|
||||||
@ -740,14 +687,15 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
widget.toUser == null
|
widget.toUser == null
|
||||||
? Builder(
|
? Builder(
|
||||||
builder: (ct) {
|
builder: (ct) {
|
||||||
return Opacity(
|
return GestureDetector(
|
||||||
opacity:
|
child: Image.asset(
|
||||||
isCpRecipientSingleMode ? 0.45 : 1,
|
isAll
|
||||||
child: GestureDetector(
|
? "sc_images/room/sc_icon_gift_all_en.png"
|
||||||
onTap:
|
: "sc_images/room/sc_icon_gift_all_no.png",
|
||||||
isCpRecipientSingleMode
|
width: 28.w,
|
||||||
? null
|
height: 28.w,
|
||||||
: () {
|
),
|
||||||
|
onTap: () {
|
||||||
isAll = !isAll;
|
isAll = !isAll;
|
||||||
for (var mai in listMai) {
|
for (var mai in listMai) {
|
||||||
mai.isSelect = isAll;
|
mai.isSelect = isAll;
|
||||||
@ -755,14 +703,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
// showGiveTypeDialog(ct);
|
// showGiveTypeDialog(ct);
|
||||||
},
|
},
|
||||||
child: Image.asset(
|
|
||||||
!isCpRecipientSingleMode && isAll
|
|
||||||
? "sc_images/room/sc_icon_gift_all_en.png"
|
|
||||||
: "sc_images/room/sc_icon_gift_all_no.png",
|
|
||||||
width: 28.w,
|
|
||||||
height: 28.w,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -867,7 +807,14 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 15.w),
|
SizedBox(width: 15.w),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: _openRoomRechargeBottomSheet,
|
onTap: () {
|
||||||
|
SmartDialog.dismiss(tag: "showGiftControl");
|
||||||
|
SCNavigatorUtils.push(
|
||||||
|
navigatorKey.currentState!.context,
|
||||||
|
WalletRoute.recharge,
|
||||||
|
replace: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
vertical: 8.w,
|
vertical: 8.w,
|
||||||
@ -1095,17 +1042,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
// 是否选中
|
// 是否选中
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (_isCpRecipientSingleMode) {
|
|
||||||
final shouldSelect = !shead.isSelect;
|
|
||||||
for (final mai in listMai) {
|
|
||||||
mai.isSelect = false;
|
|
||||||
}
|
|
||||||
shead.isSelect = shouldSelect;
|
|
||||||
isAll = false;
|
|
||||||
giveType = 1;
|
|
||||||
setState(() {});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isAll = true;
|
isAll = true;
|
||||||
for (var mai in listMai) {
|
for (var mai in listMai) {
|
||||||
if (shead.mic?.user?.id == mai.mic?.user?.id) {
|
if (shead.mic?.user?.id == mai.mic?.user?.id) {
|
||||||
@ -1207,40 +1143,18 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
///选中所有在座位上的用户
|
///选中所有在座位上的用户
|
||||||
void selecteAllMicUsers() {
|
void selecteAllMicUsers() {
|
||||||
if (_isCpRecipientSingleMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (var mai in listMai) {
|
for (var mai in listMai) {
|
||||||
mai.isSelect = true;
|
mai.isSelect = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_GiftSendRequest? _buildGiftSendRequest() {
|
_GiftSendRequest? _buildGiftSendRequest() {
|
||||||
final selectedGift = checkedGift;
|
|
||||||
if (selectedGift == null) {
|
|
||||||
_giftFxLog(
|
|
||||||
'tap send aborted reason=no_selected_gift '
|
|
||||||
'giveType=$giveType',
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final isCpGiftRequest = _isCpGift(selectedGift);
|
|
||||||
List<String> acceptUserIds = [];
|
List<String> acceptUserIds = [];
|
||||||
List<MicRes> acceptUsers = [];
|
List<MicRes> acceptUsers = [];
|
||||||
|
|
||||||
if (widget.toUser != null) {
|
if (widget.toUser != null) {
|
||||||
acceptUserIds.add(widget.toUser?.id ?? "");
|
acceptUserIds.add(widget.toUser?.id ?? "");
|
||||||
acceptUsers.add(MicRes(user: widget.toUser));
|
acceptUsers.add(MicRes(user: widget.toUser));
|
||||||
} else if (isCpGiftRequest) {
|
|
||||||
isAll = false;
|
|
||||||
giveType = 1;
|
|
||||||
for (final mu in listMai) {
|
|
||||||
if (mu.isSelect) {
|
|
||||||
acceptUsers.add(mu.mic!);
|
|
||||||
acceptUserIds.add(mu.mic!.user!.id ?? "");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
///所有在线
|
///所有在线
|
||||||
if (giveType == 2) {
|
if (giveType == 2) {
|
||||||
@ -1269,6 +1183,15 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
SCTts.show(SCAppLocalizations.of(context)!.pleaseSelectTheRecipient);
|
SCTts.show(SCAppLocalizations.of(context)!.pleaseSelectTheRecipient);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
final selectedGift = checkedGift;
|
||||||
|
if (selectedGift == null) {
|
||||||
|
_giftFxLog(
|
||||||
|
'tap send aborted reason=no_selected_gift '
|
||||||
|
'acceptUserIds=${acceptUserIds.join(",")} '
|
||||||
|
'giveType=$giveType',
|
||||||
|
);
|
||||||
|
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 =
|
||||||
@ -1549,7 +1472,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
'roomId=${request.roomId} '
|
'roomId=${request.roomId} '
|
||||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||||
);
|
);
|
||||||
_showLocalCpInviteWaitingAfterGift(request, sentUsers);
|
|
||||||
if (sentUsers.isNotEmpty) {
|
if (sentUsers.isNotEmpty) {
|
||||||
final giftBatchId = _createGiftBatchId(request);
|
final giftBatchId = _createGiftBatchId(request);
|
||||||
sendGiftMsg(
|
sendGiftMsg(
|
||||||
@ -1561,7 +1483,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
await _refreshGiftBackpack();
|
await _refreshGiftBackpack();
|
||||||
_refreshCpStateAfterGift(request, profileManager);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1573,7 +1494,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
request.quantity,
|
request.quantity,
|
||||||
false,
|
false,
|
||||||
roomId: request.roomId,
|
roomId: request.roomId,
|
||||||
relationType: request.cpRelationType,
|
|
||||||
)
|
)
|
||||||
: await repository.giveGift(
|
: await repository.giveGift(
|
||||||
request.acceptUserIds,
|
request.acceptUserIds,
|
||||||
@ -1581,7 +1501,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
request.quantity,
|
request.quantity,
|
||||||
false,
|
false,
|
||||||
roomId: request.roomId,
|
roomId: request.roomId,
|
||||||
relationType: request.cpRelationType,
|
|
||||||
);
|
);
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'$requestName success trigger=$trigger '
|
'$requestName success trigger=$trigger '
|
||||||
@ -1598,7 +1517,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
'balance=$result '
|
'balance=$result '
|
||||||
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
'elapsedMs=${stopwatch.elapsedMilliseconds}',
|
||||||
);
|
);
|
||||||
_showLocalCpInviteWaitingAfterGift(request, request.acceptUsers);
|
|
||||||
if (request.isLuckyGiftRequest) {
|
if (request.isLuckyGiftRequest) {
|
||||||
final giftBatchId = _createGiftBatchId(request);
|
final giftBatchId = _createGiftBatchId(request);
|
||||||
_showLocalLuckyGiftFeedback(
|
_showLocalLuckyGiftFeedback(
|
||||||
@ -1626,7 +1544,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
profileManager?.updateBalance(result);
|
profileManager?.updateBalance(result);
|
||||||
_refreshCpStateAfterGift(request, profileManager);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final errorMessage =
|
final errorMessage =
|
||||||
request.isBackpackGiftRequest && _isBackpackQuantityError(e)
|
request.isBackpackGiftRequest && _isBackpackQuantityError(e)
|
||||||
@ -1647,83 +1564,10 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
if (request.isBackpackGiftRequest) {
|
if (request.isBackpackGiftRequest) {
|
||||||
unawaited(_refreshGiftBackpack());
|
unawaited(_refreshGiftBackpack());
|
||||||
}
|
}
|
||||||
if (!request.isBackpackGiftRequest &&
|
|
||||||
_isInsufficientMoneyError(e, errorMessage)) {
|
|
||||||
RoomGiftComboSendController().hide();
|
|
||||||
RoomRechargeBottomSheet.show(
|
|
||||||
navigatorKey.currentState?.context ?? context,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SCTts.show(errorMessage);
|
SCTts.show(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _refreshCpStateAfterGift(
|
|
||||||
_GiftSendRequest request,
|
|
||||||
SocialChatUserProfileManager? profileManager,
|
|
||||||
) {
|
|
||||||
if (!_isCpGift(request.gift)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unawaited(profileManager?.refreshLoadedCpProfiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showLocalCpInviteWaitingAfterGift(
|
|
||||||
_GiftSendRequest request,
|
|
||||||
List<MicRes> sentUsers,
|
|
||||||
) {
|
|
||||||
if (!_isCpGift(request.gift)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final relationType = request.cpRelationType;
|
|
||||||
if (relationType == null || relationType.trim().isEmpty) {
|
|
||||||
_giftFxLog(
|
|
||||||
'local cp waiting skipped reason=no_relation_type '
|
|
||||||
'giftId=${request.gift.id} acceptUserIds=${request.acceptUserIds}',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sentUsers.isEmpty) {
|
|
||||||
_giftFxLog(
|
|
||||||
'local cp waiting skipped reason=no_sent_users '
|
|
||||||
'giftId=${request.gift.id} relationType=$relationType',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final receiverProfile = sentUsers.first.user;
|
|
||||||
if (receiverProfile == null) {
|
|
||||||
_giftFxLog(
|
|
||||||
'local cp waiting skipped reason=no_receiver_profile '
|
|
||||||
'giftId=${request.gift.id} relationType=$relationType',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final context = navigatorKey.currentState?.context;
|
|
||||||
if (context == null) {
|
|
||||||
_giftFxLog(
|
|
||||||
'local cp waiting skipped reason=no_context '
|
|
||||||
'giftId=${request.gift.id} relationType=$relationType',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_giftFxLog(
|
|
||||||
'local cp waiting trigger '
|
|
||||||
'giftId=${request.gift.id} '
|
|
||||||
'relationType=$relationType '
|
|
||||||
'receiverId=${receiverProfile.id}',
|
|
||||||
);
|
|
||||||
unawaited(
|
|
||||||
Provider.of<RtmProvider>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).showLocalCpGiftInviteWaiting(
|
|
||||||
receiverProfile: receiverProfile,
|
|
||||||
relationType: relationType,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<MicRes>> _giveBackpackGiftToRecipients(
|
Future<List<MicRes>> _giveBackpackGiftToRecipients(
|
||||||
SCChatRoomRepository repository,
|
SCChatRoomRepository repository,
|
||||||
_GiftSendRequest request,
|
_GiftSendRequest request,
|
||||||
@ -1745,7 +1589,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
roomId: request.roomId,
|
roomId: request.roomId,
|
||||||
seatIndex:
|
seatIndex:
|
||||||
acceptUser?.micIndex ?? _resolveSeatIndexForUserId(acceptUserId),
|
acceptUser?.micIndex ?? _resolveSeatIndexForUserId(acceptUserId),
|
||||||
relationType: request.cpRelationType,
|
|
||||||
);
|
);
|
||||||
if (acceptUser != null) {
|
if (acceptUser != null) {
|
||||||
sentUsers.add(acceptUser);
|
sentUsers.add(acceptUser);
|
||||||
@ -1856,7 +1699,7 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
num coins = (gift.giftCandy ?? 0) * quantity;
|
num coins = (gift.giftCandy ?? 0) * quantity;
|
||||||
if (coins > 9999 && !_isCpGift(gift)) {
|
if (coins > 9999) {
|
||||||
var fMsg = SCFloatingMessage(
|
var fMsg = SCFloatingMessage(
|
||||||
type: 1,
|
type: 1,
|
||||||
userId: AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
userId: AccountStorage().getCurrentUser()?.userProfile?.id ?? "",
|
||||||
@ -1870,7 +1713,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
toUserAvatarUrl: u.user?.userAvatar ?? "",
|
toUserAvatarUrl: u.user?.userAvatar ?? "",
|
||||||
giftUrl: gift.giftPhoto ?? "",
|
giftUrl: gift.giftPhoto ?? "",
|
||||||
giftId: gift.id,
|
giftId: gift.id,
|
||||||
giftTab: _floatingGiftTabFor(gift),
|
|
||||||
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||||||
coins: coins,
|
coins: coins,
|
||||||
number: quantity,
|
number: quantity,
|
||||||
@ -1899,6 +1741,14 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
'animationCount=$animationCount',
|
'animationCount=$animationCount',
|
||||||
);
|
);
|
||||||
SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
|
SCGiftVapSvgaManager().play(gift.giftSourceUrl!);
|
||||||
|
} else if (SCGlobalConfig.isLowPerformanceDevice &&
|
||||||
|
(rtcProvider?.shouldShowRoomVisualEffects ?? false)) {
|
||||||
|
_enqueueLowPerformanceGiftCompensation(
|
||||||
|
gift: gift,
|
||||||
|
receiver: u.user,
|
||||||
|
quantity: quantity,
|
||||||
|
coins: coins,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
_giftFxLog(
|
_giftFxLog(
|
||||||
'skip local play because visual effects disabled '
|
'skip local play because visual effects disabled '
|
||||||
@ -1925,10 +1775,51 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _enqueueLowPerformanceGiftCompensation({
|
||||||
|
required SocialChatGiftRes gift,
|
||||||
|
required SocialChatUserProfile? receiver,
|
||||||
|
required int quantity,
|
||||||
|
required num coins,
|
||||||
|
}) {
|
||||||
|
final sender = AccountStorage().getCurrentUser()?.userProfile;
|
||||||
|
final roomId = rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||||
|
_enqueueGiftFloatingMessage(
|
||||||
|
SCFloatingMessage(
|
||||||
|
type: 1,
|
||||||
|
priority: 900,
|
||||||
|
userId: sender?.id ?? "",
|
||||||
|
toUserId: receiver?.id ?? "",
|
||||||
|
userAvatarUrl: sender?.userAvatar ?? "",
|
||||||
|
toUserAvatarUrl: receiver?.userAvatar ?? "",
|
||||||
|
userName: sender?.userNickname ?? "",
|
||||||
|
toUserName: receiver?.userNickname ?? "",
|
||||||
|
giftUrl: gift.giftPhoto ?? "",
|
||||||
|
giftId: gift.id,
|
||||||
|
number: quantity,
|
||||||
|
coins: coins,
|
||||||
|
roomId: roomId,
|
||||||
|
),
|
||||||
|
dedupKey: _giftFloatingDedupKey(
|
||||||
|
roomId: roomId,
|
||||||
|
senderId: sender?.id,
|
||||||
|
receiverId: receiver?.id,
|
||||||
|
giftId: gift.id,
|
||||||
|
quantity: quantity,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _enqueueGiftFloatingMessage(
|
void _enqueueGiftFloatingMessage(
|
||||||
SCFloatingMessage message, {
|
SCFloatingMessage message, {
|
||||||
String? dedupKey,
|
String? dedupKey,
|
||||||
}) {
|
}) {
|
||||||
|
if (SCGlobalConfig.isLowPerformanceDevice) {
|
||||||
|
OverlayManager().addLowPerformanceCompensationMessage(
|
||||||
|
message,
|
||||||
|
dedupKey: dedupKey,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
OverlayManager().addMessage(message);
|
OverlayManager().addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1949,13 +1840,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
].join("|");
|
].join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
String _floatingGiftTabFor(SocialChatGiftRes gift) {
|
|
||||||
if (_isCpGift(gift)) {
|
|
||||||
return scCpRelationTypeForGift(gift);
|
|
||||||
}
|
|
||||||
return gift.giftTab ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _supportsComboFeedback(SocialChatGiftRes gift) {
|
bool _supportsComboFeedback(SocialChatGiftRes gift) {
|
||||||
final giftTab = (gift.giftTab ?? '').trim();
|
final giftTab = (gift.giftTab ?? '').trim();
|
||||||
return giftTab == "LUCK" ||
|
return giftTab == "LUCK" ||
|
||||||
@ -1964,76 +1848,6 @@ class _GiftPageState extends State<GiftPage> with TickerProviderStateMixin {
|
|||||||
giftTab == SCGiftType.MAGIC.name;
|
giftTab == SCGiftType.MAGIC.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCpGiftCloseFriendEntry() {
|
|
||||||
return Semantics(
|
|
||||||
button: true,
|
|
||||||
label: "Gifts for Close Friends",
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: _openCpRightsGuide,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 355.w,
|
|
||||||
height: 46.w,
|
|
||||||
child: Image.asset(
|
|
||||||
"$_cpProfileAssetBase/sc_cp_profile_gift_entry_card.png",
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildFirstRechargeGiftEntry() {
|
|
||||||
return Semantics(
|
|
||||||
button: true,
|
|
||||||
label: "First recharge offer",
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: _openFirstRechargeDialog,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 355.w,
|
|
||||||
height: 46.w,
|
|
||||||
child: Image.asset(_firstRechargeGiftEntryCard, fit: BoxFit.fill),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openFirstRechargeDialog() {
|
|
||||||
SmartDialog.dismiss(tag: "showGiftControl");
|
|
||||||
Future<void>.delayed(const Duration(milliseconds: 80), () {
|
|
||||||
final navigator = navigatorKey.currentState;
|
|
||||||
if (navigator == null || !navigator.mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SCDialogUtils.showFirstRechargeDialog(navigator.context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openRoomRechargeBottomSheet() {
|
|
||||||
SmartDialog.dismiss(tag: "showGiftControl");
|
|
||||||
Future<void>.delayed(const Duration(milliseconds: 80), () {
|
|
||||||
final navigator = navigatorKey.currentState;
|
|
||||||
if (navigator == null || !navigator.mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RoomRechargeBottomSheet.show(navigator.context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openCpRightsGuide() {
|
|
||||||
SmartDialog.dismiss(tag: "showGiftControl");
|
|
||||||
Future<void>.delayed(const Duration(milliseconds: 80), () {
|
|
||||||
final navigator = navigatorKey.currentState;
|
|
||||||
if (navigator == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigator.push(
|
|
||||||
MaterialPageRoute<void>(builder: (_) => const CpRightsGuidePage()),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSendButton() {
|
Widget _buildSendButton() {
|
||||||
return SCGiftComboSendButton(
|
return SCGiftComboSendButton(
|
||||||
label: SCAppLocalizations.of(context)!.send,
|
label: SCAppLocalizations.of(context)!.send,
|
||||||
@ -2296,44 +2110,6 @@ class _GiftSendRequest {
|
|||||||
return isLuckyGiftRequest ? 'giveLuckyGift' : 'giveGift';
|
return isLuckyGiftRequest ? 'giveLuckyGift' : 'giveGift';
|
||||||
}
|
}
|
||||||
|
|
||||||
String? get cpRelationType {
|
|
||||||
if (!scIsCpGift(gift)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (isSelfRecipientRequest) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return scCpRelationTypeForGift(gift);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isSelfRecipientRequest {
|
|
||||||
final currentProfile = AccountStorage().getCurrentUser()?.userProfile;
|
|
||||||
final currentKeys = _profileIdentityKeys(currentProfile);
|
|
||||||
if (currentKeys.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (final userId in acceptUserIds) {
|
|
||||||
if (currentKeys.contains(userId.trim())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final acceptUser in acceptUsers) {
|
|
||||||
final acceptKeys = _profileIdentityKeys(acceptUser.user);
|
|
||||||
if (acceptKeys.any(currentKeys.contains)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> _profileIdentityKeys(SocialChatUserProfile? profile) {
|
|
||||||
return <String>{
|
|
||||||
profile?.id?.trim() ?? "",
|
|
||||||
profile?.account?.trim() ?? "",
|
|
||||||
profile?.getID().trim() ?? "",
|
|
||||||
}..remove("");
|
|
||||||
}
|
|
||||||
|
|
||||||
String get batchKey {
|
String get batchKey {
|
||||||
final sortedAcceptUserIds = List<String>.from(acceptUserIds)..sort();
|
final sortedAcceptUserIds = List<String>.from(acceptUserIds)..sort();
|
||||||
final type =
|
final type =
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/app/config/business_logic_strategy.dart';
|
import 'package:yumi/app/config/business_logic_strategy.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
|
||||||
|
|
||||||
enum _GiftGridPageStatus { idle, loading, ready }
|
enum _GiftGridPageStatus { idle, loading, ready }
|
||||||
|
|
||||||
@ -448,15 +447,12 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
)
|
)
|
||||||
: GestureDetector(
|
: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
child: Stack(
|
child: Container(
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color:
|
color:
|
||||||
checkedIndex ==
|
checkedIndex == ref.giftByTab[widget.type]!.indexOf(gift)
|
||||||
ref.giftByTab[widget.type]!.indexOf(gift)
|
|
||||||
? SocialChatTheme.primaryLight
|
? SocialChatTheme.primaryLight
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
width: 1.w,
|
width: 1.w,
|
||||||
@ -519,14 +515,6 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_shouldShowCpRelationBadge(gift))
|
|
||||||
PositionedDirectional(
|
|
||||||
top: 0,
|
|
||||||
end: 0,
|
|
||||||
child: _buildCpRelationBadge(gift),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
checkedIndex = ref.giftByTab[widget.type]!.indexOf(gift);
|
checkedIndex = ref.giftByTab[widget.type]!.indexOf(gift);
|
||||||
@ -536,68 +524,6 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldShowCpRelationBadge(SocialChatGiftRes gift) =>
|
|
||||||
scIsCpGiftTab(widget.type) || scIsCpGift(gift);
|
|
||||||
|
|
||||||
Widget _buildCpRelationBadge(SocialChatGiftRes gift) {
|
|
||||||
final spec = _cpRelationBadgeSpec(scCpRelationTypeForGift(gift));
|
|
||||||
return Container(
|
|
||||||
width: spec.width.w,
|
|
||||||
height: 14.w,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: spec.backgroundColor,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topRight: Radius.circular(7.w),
|
|
||||||
bottomLeft: Radius.circular(7.w),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
spec.label,
|
|
||||||
maxLines: 1,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
overflow: TextOverflow.visible,
|
|
||||||
style: TextStyle(
|
|
||||||
color: spec.color,
|
|
||||||
fontFamily: spec.fontFamily,
|
|
||||||
fontSize: 10.sp,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
height: 1,
|
|
||||||
letterSpacing: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_CpRelationBadgeSpec _cpRelationBadgeSpec(String relationType) {
|
|
||||||
switch (relationType) {
|
|
||||||
case "BROTHER":
|
|
||||||
return const _CpRelationBadgeSpec(
|
|
||||||
label: "brother",
|
|
||||||
width: 44,
|
|
||||||
color: Color(0xFF024591),
|
|
||||||
backgroundColor: Color(0xFFCEE9FF),
|
|
||||||
fontFamily: "Source Han Sans SC",
|
|
||||||
);
|
|
||||||
case "SISTERS":
|
|
||||||
return const _CpRelationBadgeSpec(
|
|
||||||
label: "sister",
|
|
||||||
width: 39,
|
|
||||||
color: Color(0xFF880291),
|
|
||||||
backgroundColor: Color(0xFFEECEFF),
|
|
||||||
fontFamily: "Source Han Sans SC",
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return const _CpRelationBadgeSpec(
|
|
||||||
label: "cp",
|
|
||||||
width: 26,
|
|
||||||
color: Color(0xFFFF59B7),
|
|
||||||
backgroundColor: Color(0xFFFFCEEA),
|
|
||||||
fontFamily: "Inter",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isBackpackTab => widget.type == SCAppGeneralManager.backpackGiftTab;
|
bool get _isBackpackTab => widget.type == SCAppGeneralManager.backpackGiftTab;
|
||||||
|
|
||||||
String _formatBackpackQuantity(SocialChatGiftRes gift) {
|
String _formatBackpackQuantity(SocialChatGiftRes gift) {
|
||||||
@ -631,19 +557,3 @@ class _GiftTabPageState extends State<GiftTabPage>
|
|||||||
return Row(mainAxisAlignment: MainAxisAlignment.center, children: list);
|
return Row(mainAxisAlignment: MainAxisAlignment.center, children: list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CpRelationBadgeSpec {
|
|
||||||
const _CpRelationBadgeSpec({
|
|
||||||
required this.label,
|
|
||||||
required this.width,
|
|
||||||
required this.color,
|
|
||||||
required this.backgroundColor,
|
|
||||||
required this.fontFamily,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String label;
|
|
||||||
final double width;
|
|
||||||
final Color color;
|
|
||||||
final Color backgroundColor;
|
|
||||||
final String fontFamily;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -15,9 +15,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
import 'package:yumi/shared/tools/sc_dialog_utils.dart';
|
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
import 'package:yumi/services/general/sc_app_general_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/svga/sc_svga_asset_widget.dart';
|
import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
|
||||||
@ -46,13 +44,11 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
final List<Widget> _pages = [];
|
final List<Widget> _pages = [];
|
||||||
final Set<int> _builtPageIndexes = <int>{0};
|
final Set<int> _builtPageIndexes = <int>{0};
|
||||||
final List<BottomNavigationBarItem> _bottomItems = [];
|
final List<BottomNavigationBarItem> _bottomItems = [];
|
||||||
final ValueNotifier<int> _currentIndexNotifier = ValueNotifier<int>(0);
|
|
||||||
final ValueNotifier<int> _taskClaimableCount = ValueNotifier<int>(0);
|
final ValueNotifier<int> _taskClaimableCount = ValueNotifier<int>(0);
|
||||||
SCAppGeneralManager? generalProvider;
|
SCAppGeneralManager? generalProvider;
|
||||||
Locale? _lastLocale;
|
Locale? _lastLocale;
|
||||||
bool _hasShownEntryDialogs = false;
|
bool _hasShownEntryDialogs = false;
|
||||||
bool _hasShownRegisterRewardDialog = false;
|
bool _hasShownRegisterRewardDialog = false;
|
||||||
bool _hasRequestedFirstRechargeDialog = false;
|
|
||||||
bool _isShowingDailySignInDialog = false;
|
bool _isShowingDailySignInDialog = false;
|
||||||
bool _showRegisterRewardAfterDailySignIn = false;
|
bool _showRegisterRewardAfterDailySignIn = false;
|
||||||
bool _isOpeningFirstRegisterRoomGame = false;
|
bool _isOpeningFirstRegisterRoomGame = false;
|
||||||
@ -97,7 +93,6 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
routeObserver.unsubscribe(this);
|
routeObserver.unsubscribe(this);
|
||||||
OverlayManager().setHomeRootTabsVisible(false);
|
OverlayManager().setHomeRootTabsVisible(false);
|
||||||
_registerRewardSubscription?.cancel();
|
_registerRewardSubscription?.cancel();
|
||||||
_currentIndexNotifier.dispose();
|
|
||||||
_taskClaimableCount.dispose();
|
_taskClaimableCount.dispose();
|
||||||
WakelockPlus.disable();
|
WakelockPlus.disable();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -275,7 +270,7 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
_pages.add(SCMessagePage());
|
_pages.add(SCMessagePage());
|
||||||
_pages.add(MePage2(currentIndexListenable: _currentIndexNotifier));
|
_pages.add(MePage2());
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCachedBody() {
|
Widget _buildCachedBody() {
|
||||||
@ -302,7 +297,6 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentIndex = index;
|
_currentIndex = index;
|
||||||
_currentIndexNotifier.value = index;
|
|
||||||
_builtPageIndexes.add(index);
|
_builtPageIndexes.add(index);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -441,27 +435,14 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _showDailySignInDialogIfNeeded();
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _showEntryPopupIfNeeded();
|
await _showEntryPopupIfNeeded();
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _showFirstRechargeDialogIfNeeded();
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _openFirstRegisterRoomGameIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _showDailySignInDialogIfNeeded() async {
|
|
||||||
final dialogData = await _loadDailySignInDialogData();
|
final dialogData = await _loadDailySignInDialogData();
|
||||||
if (!mounted || dialogData == null) {
|
if (!mounted || dialogData == null) {
|
||||||
|
await _openFirstRegisterRoomGameIfNeeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_isShowingDailySignInDialog = true;
|
_isShowingDailySignInDialog = true;
|
||||||
@ -477,6 +458,7 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await _openFirstRegisterRoomGameIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showEntryPopupIfNeeded() async {
|
Future<void> _showEntryPopupIfNeeded() async {
|
||||||
@ -489,18 +471,6 @@ class _SCIndexPageState extends State<SCIndexPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showFirstRechargeDialogIfNeeded() async {
|
|
||||||
if (_hasRequestedFirstRechargeDialog ||
|
|
||||||
SCGlobalConfig.isReview ||
|
|
||||||
AccountStorage().getToken().isEmpty ||
|
|
||||||
!mounted ||
|
|
||||||
!(ModalRoute.of(context)?.isCurrent ?? false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_hasRequestedFirstRechargeDialog = true;
|
|
||||||
await SCDialogUtils.showFirstRechargeDialog(context, forceRefresh: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _waitForRegisterRewardSocketIfNeeded() async {
|
Future<void> _waitForRegisterRewardSocketIfNeeded() async {
|
||||||
if (!DataPersistence.getAwaitRegisterRewardSocket() ||
|
if (!DataPersistence.getAwaitRegisterRewardSocket() ||
|
||||||
DataPersistence.getPendingRegisterRewardDialog()) {
|
DataPersistence.getPendingRegisterRewardDialog()) {
|
||||||
|
|||||||
@ -29,13 +29,8 @@ import '../voice_room_route.dart';
|
|||||||
///编辑房间信息
|
///编辑房间信息
|
||||||
class RoomEditPage extends StatefulWidget {
|
class RoomEditPage extends StatefulWidget {
|
||||||
final String needRestCurrentRoomInfo;
|
final String needRestCurrentRoomInfo;
|
||||||
final RoomEntryPreviewData? setupPreviewData;
|
|
||||||
|
|
||||||
const RoomEditPage({
|
const RoomEditPage({super.key, this.needRestCurrentRoomInfo = "false"});
|
||||||
super.key,
|
|
||||||
this.needRestCurrentRoomInfo = "false",
|
|
||||||
this.setupPreviewData,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RoomEditPage> createState() => _RoomEditPageState();
|
State<RoomEditPage> createState() => _RoomEditPageState();
|
||||||
@ -50,7 +45,6 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
|
|
||||||
RtcProvider? rtcProvider;
|
RtcProvider? rtcProvider;
|
||||||
bool isEdit = false;
|
bool isEdit = false;
|
||||||
bool _isBasicInfoEditing = false;
|
|
||||||
final TextEditingController _roomNameController = TextEditingController();
|
final TextEditingController _roomNameController = TextEditingController();
|
||||||
final TextEditingController _roomAnnouncementController =
|
final TextEditingController _roomAnnouncementController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
@ -59,7 +53,7 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
final int _roomNameMaxLength = 24;
|
final int _roomNameMaxLength = 24;
|
||||||
int _roomNameCurrentLength = 0;
|
int _roomNameCurrentLength = 0;
|
||||||
String roomCover = "";
|
String roomCover = "";
|
||||||
final int _roomAnnouncementMaxLength = 200;
|
final int _roomAnnouncementMaxLength = 24;
|
||||||
int _roomAnnouncementCurrentLength = 0;
|
int _roomAnnouncementCurrentLength = 0;
|
||||||
bool _initialRoomLocked = false;
|
bool _initialRoomLocked = false;
|
||||||
bool _isRoomPrivate = false;
|
bool _isRoomPrivate = false;
|
||||||
@ -82,25 +76,17 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
final setupPreviewData = widget.setupPreviewData;
|
|
||||||
_roomNameController.text =
|
_roomNameController.text =
|
||||||
setupPreviewData?.roomName ??
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomName ?? "";
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomName ??
|
|
||||||
"";
|
|
||||||
_roomNameCurrentLength = _getActualCharacterCount(_roomNameController.text);
|
_roomNameCurrentLength = _getActualCharacterCount(_roomNameController.text);
|
||||||
_roomAnnouncementController.text =
|
_roomAnnouncementController.text =
|
||||||
setupPreviewData?.roomDesc ??
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomDesc ?? "";
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomDesc ??
|
|
||||||
"";
|
|
||||||
_roomAnnouncementCurrentLength = _getActualCharacterCount(
|
_roomAnnouncementCurrentLength = _getActualCharacterCount(
|
||||||
_roomAnnouncementController.text,
|
_roomAnnouncementController.text,
|
||||||
);
|
);
|
||||||
roomCover =
|
roomCover =
|
||||||
setupPreviewData?.roomCover ??
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomCover ?? "";
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomCover ??
|
|
||||||
"";
|
|
||||||
final roomPassword =
|
final roomPassword =
|
||||||
setupPreviewData?.roomSetting?.password?.trim() ??
|
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomSetting?.password?.trim() ??
|
rtcProvider?.currenRoom?.roomProfile?.roomSetting?.password?.trim() ??
|
||||||
"";
|
"";
|
||||||
_initialRoomLocked = roomPassword.isNotEmpty;
|
_initialRoomLocked = roomPassword.isNotEmpty;
|
||||||
@ -147,11 +133,22 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
SizedBox(height: mediaQuery.viewPadding.top),
|
SizedBox(height: mediaQuery.viewPadding.top),
|
||||||
_buildNavigationBar(context),
|
_buildNavigationBar(context),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: Stack(
|
||||||
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
children: [
|
||||||
padding: EdgeInsets.only(bottom: bottomPadding + 24.w),
|
SingleChildScrollView(
|
||||||
|
keyboardDismissBehavior:
|
||||||
|
ScrollViewKeyboardDismissBehavior.onDrag,
|
||||||
|
padding: EdgeInsets.only(bottom: bottomPadding + 116.w),
|
||||||
child: _buildForm(context),
|
child: _buildForm(context),
|
||||||
),
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 24.w,
|
||||||
|
right: 24.w,
|
||||||
|
bottom: bottomPadding + 14.w,
|
||||||
|
child: _buildSaveButton(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -198,50 +195,11 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
height: 1,
|
height: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PositionedDirectional(
|
|
||||||
end: 16.w,
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: _buildNavigationAction(context),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNavigationAction(BuildContext context) {
|
|
||||||
final label =
|
|
||||||
_isBasicInfoEditing || isEdit
|
|
||||||
? SCAppLocalizations.of(context)!.save
|
|
||||||
: SCAppLocalizations.of(context)!.edit;
|
|
||||||
return SCDebounceWidget(
|
|
||||||
onTap: () {
|
|
||||||
if (!_isBasicInfoEditing && !isEdit) {
|
|
||||||
setState(() {
|
|
||||||
_isBasicInfoEditing = true;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
submit(context);
|
|
||||||
},
|
|
||||||
child: SizedBox(
|
|
||||||
height: 44.w,
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: SocialChatTheme.primaryColor,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
height: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildForm(BuildContext context) {
|
Widget _buildForm(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
@ -253,12 +211,11 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
height: 40.w,
|
height: 40.w,
|
||||||
hintText: "Please enter a name",
|
hintText: "Please enter a name",
|
||||||
counterText: '$_roomNameCurrentLength/$_roomNameMaxLength',
|
counterText: '$_roomNameCurrentLength/$_roomNameMaxLength',
|
||||||
readOnly: !_isBasicInfoEditing,
|
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
SCAccurateLengthLimitingTextInputFormatter(_roomNameMaxLength),
|
SCAccurateLengthLimitingTextInputFormatter(_roomNameMaxLength),
|
||||||
],
|
],
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
_markEdited();
|
isEdit = true;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.w),
|
SizedBox(height: 16.w),
|
||||||
@ -274,22 +231,17 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
hintText: "Please enter a name",
|
hintText: "Please enter a name",
|
||||||
maxLines: null,
|
maxLines: null,
|
||||||
expands: true,
|
expands: true,
|
||||||
readOnly: !_isBasicInfoEditing,
|
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
SCAccurateLengthLimitingTextInputFormatter(
|
SCAccurateLengthLimitingTextInputFormatter(
|
||||||
_roomAnnouncementMaxLength,
|
_roomAnnouncementMaxLength,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
_markEdited();
|
isEdit = true;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 16.w),
|
SizedBox(height: 16.w),
|
||||||
_buildLockHeader(),
|
_buildLockHeader(),
|
||||||
Visibility(
|
|
||||||
visible: _isRoomPrivate,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(height: 8.w),
|
SizedBox(height: 8.w),
|
||||||
_buildTextInput(
|
_buildTextInput(
|
||||||
controller: _roomLockPasswordController,
|
controller: _roomLockPasswordController,
|
||||||
@ -298,14 +250,11 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
_markEdited();
|
isEdit = true;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 8.w),
|
SizedBox(height: 8.w),
|
||||||
_buildLockTips(),
|
_buildLockTips(),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (widget.needRestCurrentRoomInfo != "true") ...[
|
if (widget.needRestCurrentRoomInfo != "true") ...[
|
||||||
SizedBox(height: 16.w),
|
SizedBox(height: 16.w),
|
||||||
_buildSettingRow(
|
_buildSettingRow(
|
||||||
@ -391,7 +340,6 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
TextInputType? keyboardType,
|
TextInputType? keyboardType,
|
||||||
List<TextInputFormatter>? inputFormatters,
|
List<TextInputFormatter>? inputFormatters,
|
||||||
ValueChanged<String>? onChanged,
|
ValueChanged<String>? onChanged,
|
||||||
bool readOnly = false,
|
|
||||||
}) {
|
}) {
|
||||||
final isMultiline = maxLines == null || expands;
|
final isMultiline = maxLines == null || expands;
|
||||||
return Container(
|
return Container(
|
||||||
@ -417,9 +365,6 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
cursorColor: SocialChatTheme.primaryColor,
|
cursorColor: SocialChatTheme.primaryColor,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
obscureText: obscureText,
|
obscureText: obscureText,
|
||||||
readOnly: readOnly,
|
|
||||||
enableInteractiveSelection: !readOnly,
|
|
||||||
showCursor: !readOnly,
|
|
||||||
inputFormatters: inputFormatters,
|
inputFormatters: inputFormatters,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
expands: expands,
|
expands: expands,
|
||||||
@ -480,15 +425,6 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _markEdited() {
|
|
||||||
if (isEdit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
isEdit = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSectionHeader({required String title, String? counterText}) {
|
Widget _buildSectionHeader({required String title, String? counterText}) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||||
@ -691,9 +627,34 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildSaveButton(BuildContext context) {
|
||||||
|
return SCDebounceWidget(
|
||||||
|
onTap: () {
|
||||||
|
submit(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
height: 44.w,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: SocialChatTheme.primaryColor,
|
||||||
|
borderRadius: BorderRadius.circular(22.w),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
SCAppLocalizations.of(context)!.save,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 20.sp,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _handleBack() {
|
void _handleBack() {
|
||||||
if (widget.needRestCurrentRoomInfo == "true" &&
|
if (widget.needRestCurrentRoomInfo == "true") {
|
||||||
widget.setupPreviewData == null) {
|
|
||||||
Provider.of<RtcProvider>(context, listen: false).currenRoom = null;
|
Provider.of<RtcProvider>(context, listen: false).currenRoom = null;
|
||||||
}
|
}
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@ -739,35 +700,11 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
);
|
);
|
||||||
final currentRtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
final currentRtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||||||
final currentRtmProvider = Provider.of<RtmProvider>(context, listen: false);
|
final currentRtmProvider = Provider.of<RtmProvider>(context, listen: false);
|
||||||
final setupPreviewData = widget.setupPreviewData;
|
|
||||||
final currentRoomProfile =
|
|
||||||
rtcProvider?.currenRoom?.roomProfile?.roomProfile;
|
|
||||||
final currentRoom = rtcProvider?.currenRoom?.roomProfile;
|
|
||||||
final submittedRoomName = _roomNameController.text.trim();
|
final submittedRoomName = _roomNameController.text.trim();
|
||||||
final submittedRoomDesc = _roomAnnouncementController.text.trim();
|
final submittedRoomDesc = _roomAnnouncementController.text.trim();
|
||||||
final submittedRoomCover = roomCover.trim();
|
final submittedRoomCover = roomCover.trim();
|
||||||
final submittedRoomPassword = _roomLockPasswordController.text.trim();
|
final submittedRoomPassword = _roomLockPasswordController.text.trim();
|
||||||
final roomId = setupPreviewData?.roomId ?? currentRoomProfile?.id ?? "";
|
final roomId = rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "";
|
||||||
final roomAccount =
|
|
||||||
setupPreviewData?.roomAccount ?? currentRoomProfile?.roomAccount ?? "";
|
|
||||||
final roomUserId = setupPreviewData?.userId ?? currentRoomProfile?.userId;
|
|
||||||
final roomSetting =
|
|
||||||
setupPreviewData?.roomSetting ?? currentRoom?.roomSetting;
|
|
||||||
final ownerProfile =
|
|
||||||
setupPreviewData?.ownerProfile ?? currentRoom?.userProfile;
|
|
||||||
final roomAdminCount =
|
|
||||||
setupPreviewData?.roomAdminCount ??
|
|
||||||
currentRoom?.roomCounter?.adminCount;
|
|
||||||
final roomMemberCount =
|
|
||||||
setupPreviewData?.roomMemberCount ??
|
|
||||||
currentRoom?.roomCounter?.memberCount;
|
|
||||||
final entrantsRole =
|
|
||||||
setupPreviewData?.entrantsRole ??
|
|
||||||
rtcProvider?.currenRoom?.entrants?.roles;
|
|
||||||
if (roomId.trim().isEmpty || roomAccount.trim().isEmpty) {
|
|
||||||
SCTts.show("Room data is not ready");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (submittedRoomName.isEmpty) {
|
if (submittedRoomName.isEmpty) {
|
||||||
SCTts.show("Room name not empty!");
|
SCTts.show("Room name not empty!");
|
||||||
return;
|
return;
|
||||||
@ -796,9 +733,6 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final mergedRoomInfo = roomInfo.copyWith(
|
final mergedRoomInfo = roomInfo.copyWith(
|
||||||
id: _preferNonEmpty(roomInfo.id, roomId),
|
|
||||||
roomAccount: _preferNonEmpty(roomInfo.roomAccount, roomAccount),
|
|
||||||
userId: roomInfo.userId ?? roomUserId,
|
|
||||||
roomCover: _preferNonEmpty(roomInfo.roomCover, submittedRoomCover),
|
roomCover: _preferNonEmpty(roomInfo.roomCover, submittedRoomCover),
|
||||||
roomName: _preferNonEmpty(roomInfo.roomName, submittedRoomName),
|
roomName: _preferNonEmpty(roomInfo.roomName, submittedRoomName),
|
||||||
roomDesc: _preferNonEmpty(roomInfo.roomDesc, submittedRoomDesc),
|
roomDesc: _preferNonEmpty(roomInfo.roomDesc, submittedRoomDesc),
|
||||||
@ -827,7 +761,7 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
if (widget.needRestCurrentRoomInfo == "true") {
|
if (widget.needRestCurrentRoomInfo == "true") {
|
||||||
///需要创建群组
|
///需要创建群组
|
||||||
var c = await currentRtmProvider.createRoomGroup(
|
var c = await currentRtmProvider.createRoomGroup(
|
||||||
roomAccount,
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "",
|
||||||
mergedRoomInfo.roomName ?? submittedRoomName,
|
mergedRoomInfo.roomName ?? submittedRoomName,
|
||||||
);
|
);
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
@ -836,25 +770,31 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
}
|
}
|
||||||
if (_roomGroupReadyCodes.contains(c.code)) {
|
if (_roomGroupReadyCodes.contains(c.code)) {
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.createRoomSuccsess);
|
|
||||||
SCNavigatorUtils.goBack(context);
|
SCNavigatorUtils.goBack(context);
|
||||||
currentRtcProvider.joinVoiceRoomSession(
|
currentRtcProvider.joinVoiceRoomSession(
|
||||||
context,
|
context,
|
||||||
mergedRoomInfo.id ?? roomId,
|
mergedRoomInfo.id ??
|
||||||
clearRoomData: setupPreviewData == null,
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ??
|
||||||
|
"",
|
||||||
|
clearRoomData: true,
|
||||||
previewData: RoomEntryPreviewData(
|
previewData: RoomEntryPreviewData(
|
||||||
roomId: mergedRoomInfo.id ?? roomId,
|
roomId:
|
||||||
roomAccount: mergedRoomInfo.roomAccount ?? roomAccount,
|
mergedRoomInfo.id ??
|
||||||
userId: mergedRoomInfo.userId ?? roomUserId,
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id,
|
||||||
|
roomAccount:
|
||||||
|
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount,
|
||||||
|
userId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.userId,
|
||||||
roomCover: mergedRoomInfo.roomCover,
|
roomCover: mergedRoomInfo.roomCover,
|
||||||
roomName: mergedRoomInfo.roomName ?? submittedRoomName,
|
roomName: mergedRoomInfo.roomName ?? submittedRoomName,
|
||||||
roomDesc: mergedRoomInfo.roomDesc ?? submittedRoomDesc,
|
roomDesc: mergedRoomInfo.roomDesc ?? submittedRoomDesc,
|
||||||
roomSetting: roomSetting,
|
roomSetting: rtcProvider?.currenRoom?.roomProfile?.roomSetting,
|
||||||
existsPassword: _isRoomPrivate,
|
existsPassword: _isRoomPrivate,
|
||||||
ownerProfile: ownerProfile,
|
ownerProfile: rtcProvider?.currenRoom?.roomProfile?.userProfile,
|
||||||
roomAdminCount: roomAdminCount,
|
roomAdminCount:
|
||||||
roomMemberCount: roomMemberCount,
|
rtcProvider?.currenRoom?.roomProfile?.roomCounter?.adminCount,
|
||||||
entrantsRole: entrantsRole,
|
roomMemberCount:
|
||||||
|
rtcProvider?.currenRoom?.roomProfile?.roomCounter?.memberCount,
|
||||||
|
entrantsRole: rtcProvider?.currenRoom?.entrants?.roles,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -558,14 +558,9 @@ class _SwipeDeleteTileState extends State<_SwipeDeleteTile> {
|
|||||||
duration: const Duration(milliseconds: 160),
|
duration: const Duration(milliseconds: 160),
|
||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
transform: Matrix4.translationValues(_dragOffset, 0, 0),
|
transform: Matrix4.translationValues(_dragOffset, 0, 0),
|
||||||
child: SizedBox(
|
|
||||||
width: constraints.maxWidth + actionWidth,
|
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(width: constraints.maxWidth, child: widget.child),
|
||||||
width: constraints.maxWidth,
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: actionWidth,
|
width: actionWidth,
|
||||||
height: 56.w,
|
height: 56.w,
|
||||||
@ -592,7 +587,6 @@ class _SwipeDeleteTileState extends State<_SwipeDeleteTile> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import '../../../ui_kit/components/sc_page_list.dart';
|
|||||||
import '../../../ui_kit/components/sc_tts.dart';
|
import '../../../ui_kit/components/sc_tts.dart';
|
||||||
import '../../../ui_kit/components/text/sc_text.dart';
|
import '../../../ui_kit/components/text/sc_text.dart';
|
||||||
import '../../../ui_kit/widgets/id/sc_special_id_badge.dart';
|
import '../../../ui_kit/widgets/id/sc_special_id_badge.dart';
|
||||||
|
import '../../../shared/tools/sc_lk_dialog_util.dart';
|
||||||
import '../../../ui_kit/widgets/badge/sc_user_badge_strip.dart';
|
import '../../../ui_kit/widgets/badge/sc_user_badge_strip.dart';
|
||||||
import '../../../ui_kit/widgets/room/room_user_info_card.dart';
|
import '../../../ui_kit/widgets/room/room_user_info_card.dart';
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class _RoomOnlinePageState
|
|||||||
if (dialogContext == null) {
|
if (dialogContext == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RoomUserInfoCard.showPreloaded(dialogContext, userId: userId);
|
showBottomInBottomDialog(dialogContext, RoomUserInfoCard(userId: userId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
|||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/room_gift_rank_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/room_gift_rank_res.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/id/sc_special_id_badge.dart';
|
import 'package:yumi/ui_kit/widgets/id/sc_special_id_badge.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_user_info_card.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_user_info_card.dart';
|
||||||
@ -97,11 +98,11 @@ class _RoomGiftRankTabPageState
|
|||||||
// headdress: userInfo.userProfile?.getHeaddress()?.sourceUrl,
|
// headdress: userInfo.userProfile?.getHeaddress()?.sourceUrl,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SmartDialog.dismiss(tag: "showRoomGiftRankPage");
|
showBottomInBottomDialog(
|
||||||
RoomUserInfoCard.showPreloaded(
|
|
||||||
navigatorKey.currentState!.context,
|
navigatorKey.currentState!.context,
|
||||||
userId: userProfile?.id,
|
RoomUserInfoCard(userId: userProfile?.id),
|
||||||
);
|
);
|
||||||
|
SmartDialog.dismiss(tag: "showRoomGiftRankPage");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(width: 3.w),
|
SizedBox(width: 3.w),
|
||||||
|
|||||||
@ -225,7 +225,7 @@ class _SCSeatItemState extends State<SCSeatItem> with TickerProviderStateMixin {
|
|||||||
height: _seatLabelRowHeight.w,
|
height: _seatLabelRowHeight.w,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: text(
|
child: text(
|
||||||
"NO.${widget.index.toInt() + 1}",
|
"NO.${widget.index}",
|
||||||
fontSize: 10.sp,
|
fontSize: 10.sp,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -21,19 +21,15 @@ import 'package:yumi/services/music/room_music_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_gift_vap_svga_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_lucky_gift_win_sound_player.dart';
|
import 'package:yumi/shared/tools/sc_lucky_gift_win_sound_player.dart';
|
||||||
import 'package:yumi/shared/tools/sc_cp_gift_relation_utils.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_network_image_utils.dart';
|
import 'package:yumi/shared/tools/sc_network_image_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_effect_scheduler.dart';
|
import 'package:yumi/shared/tools/sc_room_effect_scheduler.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/models/message/sc_floating_message.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.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';
|
||||||
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_screen.dart';
|
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_screen.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_widget.dart';
|
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_widget.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/effect/luck_gift_nomor_anim_widget.dart';
|
import 'package:yumi/ui_kit/widgets/room/effect/luck_gift_nomor_anim_widget.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/effect/vapp_svga_layer_widget.dart';
|
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_head_widget.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_head_widget.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/ui_kit/widgets/room/room_online_user_widget.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_online_user_widget.dart';
|
||||||
@ -81,16 +77,18 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
int _shownRoomStartupFailureToken = 0;
|
int _shownRoomStartupFailureToken = 0;
|
||||||
RtcProvider? _rtcProvider;
|
RtcProvider? _rtcProvider;
|
||||||
bool _roomProviderRebuildScheduled = false;
|
bool _roomProviderRebuildScheduled = false;
|
||||||
bool _roomRouteVisibleScheduled = false;
|
|
||||||
bool _roomVisualEffectsEnsureScheduled = false;
|
|
||||||
PageRoute<dynamic>? _routeObserverRoute;
|
PageRoute<dynamic>? _routeObserverRoute;
|
||||||
SCRoomTopLayerGuardLease? _coveredRouteGuardLease;
|
|
||||||
bool _roomRouteVisible = false;
|
bool _roomRouteVisible = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(length: _chatTabCount, vsync: this);
|
_tabController = TabController(length: _chatTabCount, vsync: this);
|
||||||
|
Provider.of<RtcProvider>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).setVoiceRoomRouteVisible(true);
|
||||||
|
_enableRoomVisualEffects();
|
||||||
_tabController.addListener(_handleTabChange);
|
_tabController.addListener(_handleTabChange);
|
||||||
_subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen((
|
_subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen((
|
||||||
event,
|
event,
|
||||||
@ -126,20 +124,15 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
}
|
}
|
||||||
_routeObserverRoute = route;
|
_routeObserverRoute = route;
|
||||||
routeObserver.subscribe(this, route);
|
routeObserver.subscribe(this, route);
|
||||||
if (route.isCurrent) {
|
_setRoomRouteVisible(route.isCurrent);
|
||||||
_scheduleRoomRouteVisible();
|
|
||||||
} else {
|
|
||||||
_handleRoomRouteCovered();
|
|
||||||
}
|
}
|
||||||
}
|
_ensureRoomVisualEffectsEnabled();
|
||||||
_scheduleEnsureRoomVisualEffectsEnabled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
routeObserver.unsubscribe(this);
|
routeObserver.unsubscribe(this);
|
||||||
_routeObserverRoute = null;
|
_routeObserverRoute = null;
|
||||||
_releaseCoveredRouteGuard();
|
|
||||||
_setRoomRouteVisible(false);
|
_setRoomRouteVisible(false);
|
||||||
_rtcProvider?.removeListener(_handleRoomProviderChanged);
|
_rtcProvider?.removeListener(_handleRoomProviderChanged);
|
||||||
_rtcProvider = null;
|
_rtcProvider = null;
|
||||||
@ -153,60 +146,22 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didPush() {
|
void didPush() {
|
||||||
_handleRoomRouteVisible();
|
_setRoomRouteVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPopNext() {
|
void didPopNext() {
|
||||||
_handleRoomRouteVisible();
|
_setRoomRouteVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPushNext() {
|
void didPushNext() {
|
||||||
_handleRoomRouteCovered();
|
_setRoomRouteVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPop() {
|
void didPop() {
|
||||||
_handleRoomRouteCovered();
|
|
||||||
_releaseCoveredRouteGuard();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleRoomRouteVisible() {
|
|
||||||
_releaseCoveredRouteGuard();
|
|
||||||
_setRoomRouteVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _scheduleRoomRouteVisible() {
|
|
||||||
if (_roomRouteVisibleScheduled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomRouteVisibleScheduled = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_roomRouteVisibleScheduled = false;
|
|
||||||
if (!mounted || !(ModalRoute.of(context)?.isCurrent ?? false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_handleRoomRouteVisible();
|
|
||||||
_ensureRoomVisualEffectsEnabled();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleRoomRouteCovered() {
|
|
||||||
_setRoomRouteVisible(false);
|
_setRoomRouteVisible(false);
|
||||||
_acquireCoveredRouteGuard();
|
|
||||||
_clearTransientRoomVisualEffects(reason: 'voice_room_route_covered');
|
|
||||||
}
|
|
||||||
|
|
||||||
void _acquireCoveredRouteGuard() {
|
|
||||||
_coveredRouteGuardLease ??= SCRoomTopLayerGuard().acquire(
|
|
||||||
reason: 'voice_room_route_covered',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _releaseCoveredRouteGuard() {
|
|
||||||
_coveredRouteGuardLease?.release();
|
|
||||||
_coveredRouteGuardLease = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setRoomRouteVisible(bool visible) {
|
void _setRoomRouteVisible(bool visible) {
|
||||||
@ -261,43 +216,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
_enableRoomVisualEffects();
|
_enableRoomVisualEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _scheduleEnsureRoomVisualEffectsEnabled() {
|
|
||||||
if (_roomVisualEffectsEnsureScheduled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_roomVisualEffectsEnsureScheduled = true;
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
_roomVisualEffectsEnsureScheduled = false;
|
|
||||||
if (!mounted || !(ModalRoute.of(context)?.isCurrent ?? false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ensureRoomVisualEffectsEnabled();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _clearTransientRoomVisualEffects({required String reason}) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
|
||||||
final currentRoomId =
|
|
||||||
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? '';
|
|
||||||
|
|
||||||
RoomEntranceHelper.clearQueue();
|
|
||||||
Provider.of<GiftProvider>(context, listen: false).clearAllGiftData();
|
|
||||||
Provider.of<GiftAnimationManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).clearActiveAnimations();
|
|
||||||
_clearLuckyGiftComboSessions();
|
|
||||||
_clearGiftFlightBatchDedupeTimers();
|
|
||||||
_clearGiftVisualBatchDedupeTimers();
|
|
||||||
_giftSeatFlightController.clear();
|
|
||||||
OverlayManager().removeRoom(roomId: currentRoomId);
|
|
||||||
SCRoomEffectScheduler().clearDeferredTasks(reason: reason);
|
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _suspendRoomVisualEffects() {
|
void _suspendRoomVisualEffects() {
|
||||||
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
final rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||||||
final currentRoomId =
|
final currentRoomId =
|
||||||
@ -322,12 +240,10 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
OverlayManager().removeRoom(roomId: currentRoomId);
|
OverlayManager().removeRoom(roomId: currentRoomId);
|
||||||
SCRoomEffectScheduler().clearDeferredTasks(reason: 'voice_room_suspend');
|
SCRoomEffectScheduler().clearDeferredTasks(reason: 'voice_room_suspend');
|
||||||
SCGiftVapSvgaManager().stopPlayback();
|
SCGiftVapSvgaManager().stopPlayback();
|
||||||
if (!SCFloatIchart().isShow()) {
|
|
||||||
unawaited(
|
unawaited(
|
||||||
Provider.of<RoomMusicManager>(context, listen: false).stopForRoomExit(),
|
Provider.of<RoomMusicManager>(context, listen: false).stopForRoomExit(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRoomStartupLayer() {
|
Widget _buildRoomStartupLayer() {
|
||||||
return Selector<RtcProvider, _RoomStartupSnapshot>(
|
return Selector<RtcProvider, _RoomStartupSnapshot>(
|
||||||
@ -389,8 +305,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
return SCAppLocalizations.of(context)!.enterRoomFailedRetry;
|
return SCAppLocalizations.of(context)!.enterRoomFailedRetry;
|
||||||
case RoomStartupFailureType.rtc:
|
case RoomStartupFailureType.rtc:
|
||||||
return SCAppLocalizations.of(context)!.voiceConnectionFailedRetry;
|
return SCAppLocalizations.of(context)!.voiceConnectionFailedRetry;
|
||||||
case RoomStartupFailureType.kicked:
|
|
||||||
return SCAppLocalizations.of(context)!.roomKickedEntryBlockedTips;
|
|
||||||
case RoomStartupFailureType.entry:
|
case RoomStartupFailureType.entry:
|
||||||
case RoomStartupFailureType.none:
|
case RoomStartupFailureType.none:
|
||||||
return SCAppLocalizations.of(context)!.operationFail;
|
return SCAppLocalizations.of(context)!.operationFail;
|
||||||
@ -504,8 +418,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
// _buildPlayViews(),
|
// _buildPlayViews(),
|
||||||
///幸运礼物中奖动画
|
///幸运礼物中奖动画
|
||||||
LuckGiftNomorAnimWidget(),
|
LuckGiftNomorAnimWidget(),
|
||||||
_buildRoomGiftSvgaLayer(),
|
|
||||||
_buildRoomGiftSeatFlightLayer(),
|
|
||||||
const RoomGiftComboFloatingLayer(),
|
const RoomGiftComboFloatingLayer(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -514,43 +426,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRoomGiftSvgaLayer() {
|
|
||||||
if (!SCGlobalConfig.allowsHighCostAnimations) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return Positioned.fill(
|
|
||||||
child: Selector<RtcProvider, bool>(
|
|
||||||
selector: (context, provider) => provider.shouldShowRoomVisualEffects,
|
|
||||||
builder: (context, shouldShowRoomVisualEffects, child) {
|
|
||||||
if (!shouldShowRoomVisualEffects) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return child!;
|
|
||||||
},
|
|
||||||
child: const VapPlusSvgaPlayer(tag: "room_gift"),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRoomGiftSeatFlightLayer() {
|
|
||||||
return Positioned.fill(
|
|
||||||
child: Selector<RtcProvider, bool>(
|
|
||||||
selector: (context, provider) => provider.shouldShowRoomVisualEffects,
|
|
||||||
builder: (context, shouldShowRoomVisualEffects, child) {
|
|
||||||
if (!shouldShowRoomVisualEffects) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
return RoomGiftSeatFlightOverlay(
|
|
||||||
controller: _giftSeatFlightController,
|
|
||||||
resolveTargetKey:
|
|
||||||
(userId) =>
|
|
||||||
context.read<RtcProvider>().getSeatGlobalKeyByIndex(userId),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
///消息
|
///消息
|
||||||
Widget _buildChatView(String chatRoomKey) {
|
Widget _buildChatView(String chatRoomKey) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
@ -732,7 +607,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
final giftPhoto = (msg.gift?.giftPhoto ?? "").trim();
|
final giftPhoto = (msg.gift?.giftPhoto ?? "").trim();
|
||||||
_enqueueCpGiftFloatingIfNeeded(msg);
|
|
||||||
if (_supportsComboMilestoneEffects(msg)) {
|
if (_supportsComboMilestoneEffects(msg)) {
|
||||||
_handleComboMilestoneVisuals(msg, targetGroupKey);
|
_handleComboMilestoneVisuals(msg, targetGroupKey);
|
||||||
}
|
}
|
||||||
@ -754,48 +628,6 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _enqueueCpGiftFloatingIfNeeded(Msg msg) {
|
|
||||||
final gift = msg.gift;
|
|
||||||
if (!scIsCpGift(gift) || gift == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final quantity = msg.number ?? 0;
|
|
||||||
if (quantity <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final roomId = _resolveGiftFloatingRoomId(msg);
|
|
||||||
OverlayManager().addMessage(
|
|
||||||
SCFloatingMessage(
|
|
||||||
type: 1,
|
|
||||||
priority: 900,
|
|
||||||
userId: msg.user?.id ?? "",
|
|
||||||
toUserId: msg.toUser?.id ?? "",
|
|
||||||
userAvatarUrl: msg.user?.userAvatar ?? "",
|
|
||||||
toUserAvatarUrl: msg.toUser?.userAvatar ?? "",
|
|
||||||
userName: msg.user?.userNickname ?? "",
|
|
||||||
toUserName: msg.toUser?.userNickname ?? "",
|
|
||||||
giftUrl: gift.giftPhoto ?? "",
|
|
||||||
giftId: gift.id,
|
|
||||||
giftTab: scCpRelationTypeForGift(gift),
|
|
||||||
number: quantity,
|
|
||||||
coins: quantity * (gift.giftCandy ?? 0),
|
|
||||||
roomId: roomId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _resolveGiftFloatingRoomId(Msg msg) {
|
|
||||||
final messageRoomId = (msg.msg ?? "").trim();
|
|
||||||
if (messageRoomId.isNotEmpty) {
|
|
||||||
return messageRoomId;
|
|
||||||
}
|
|
||||||
return Provider.of<RtcProvider>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).currenRoom?.roomProfile?.roomProfile?.id ??
|
|
||||||
"";
|
|
||||||
}
|
|
||||||
|
|
||||||
void _luckyGiftRewardTickerListener(Msg msg) {
|
void _luckyGiftRewardTickerListener(Msg msg) {
|
||||||
if (!Provider.of<RtcProvider>(
|
if (!Provider.of<RtcProvider>(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@ -159,14 +159,12 @@ class VoiceRoomRoute implements SCIRouterProvider {
|
|||||||
static Future<T?> openRoomEdit<T>(
|
static Future<T?> openRoomEdit<T>(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required bool needRestCurrentRoomInfo,
|
required bool needRestCurrentRoomInfo,
|
||||||
RoomEntryPreviewData? setupPreviewData,
|
|
||||||
bool rootNavigator = true,
|
bool rootNavigator = true,
|
||||||
}) {
|
}) {
|
||||||
return Navigator.of(context, rootNavigator: rootNavigator).push<T>(
|
return Navigator.of(context, rootNavigator: rootNavigator).push<T>(
|
||||||
_buildRoute<T>(
|
_buildRoute<T>(
|
||||||
RoomEditPage(
|
RoomEditPage(
|
||||||
needRestCurrentRoomInfo: needRestCurrentRoomInfo ? "true" : "false",
|
needRestCurrentRoomInfo: needRestCurrentRoomInfo ? "true" : "false",
|
||||||
setupPreviewData: setupPreviewData,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,303 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
class HotgameBridgeActions {
|
|
||||||
static const String recharge = 'recharge';
|
|
||||||
static const String quit = 'quit';
|
|
||||||
static const String loadComplete = 'loadComplete';
|
|
||||||
static const String debugLog = 'debugLog';
|
|
||||||
|
|
||||||
static String normalize(String action) {
|
|
||||||
final trimmed = action.trim();
|
|
||||||
final lower = trimmed.toLowerCase();
|
|
||||||
switch (lower) {
|
|
||||||
case 'recharge':
|
|
||||||
case 'pay':
|
|
||||||
case 'gamepay':
|
|
||||||
case 'gamerecharge':
|
|
||||||
case 'insufficient':
|
|
||||||
case 'insufficientbalance':
|
|
||||||
return recharge;
|
|
||||||
case 'quit':
|
|
||||||
case 'exit':
|
|
||||||
case 'close':
|
|
||||||
case 'closegame':
|
|
||||||
case 'destroy':
|
|
||||||
return quit;
|
|
||||||
case 'loadcomplete':
|
|
||||||
case 'gameloaded':
|
|
||||||
case 'loaded':
|
|
||||||
case 'ready':
|
|
||||||
return loadComplete;
|
|
||||||
case 'debuglog':
|
|
||||||
return debugLog;
|
|
||||||
default:
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HotgameBridgeMessage {
|
|
||||||
const HotgameBridgeMessage({
|
|
||||||
required this.action,
|
|
||||||
this.payload = const <String, dynamic>{},
|
|
||||||
});
|
|
||||||
|
|
||||||
final String action;
|
|
||||||
final Map<String, dynamic> payload;
|
|
||||||
|
|
||||||
static HotgameBridgeMessage fromAction(String action, String rawMessage) {
|
|
||||||
return HotgameBridgeMessage(
|
|
||||||
action: HotgameBridgeActions.normalize(action),
|
|
||||||
payload: _parsePayload(rawMessage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static HotgameBridgeMessage parse(String rawMessage) {
|
|
||||||
try {
|
|
||||||
final dynamic decoded = jsonDecode(rawMessage);
|
|
||||||
if (decoded is Map<String, dynamic>) {
|
|
||||||
return HotgameBridgeMessage(
|
|
||||||
action: HotgameBridgeActions.normalize(
|
|
||||||
(decoded['action'] ??
|
|
||||||
decoded['cmd'] ??
|
|
||||||
decoded['event'] ??
|
|
||||||
decoded['method'] ??
|
|
||||||
decoded['type'] ??
|
|
||||||
'')
|
|
||||||
.toString(),
|
|
||||||
),
|
|
||||||
payload: _parsePayload(
|
|
||||||
decoded['payload'] ?? decoded['data'] ?? decoded['params'],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
return HotgameBridgeMessage(
|
|
||||||
action: HotgameBridgeActions.normalize(rawMessage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, dynamic> _parsePayload(dynamic rawPayload) {
|
|
||||||
if (rawPayload is Map<String, dynamic>) {
|
|
||||||
return rawPayload;
|
|
||||||
}
|
|
||||||
if (rawPayload is String) {
|
|
||||||
final trimmed = rawPayload.trim();
|
|
||||||
if (trimmed.isEmpty) {
|
|
||||||
return const <String, dynamic>{};
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final dynamic decoded = jsonDecode(trimmed);
|
|
||||||
if (decoded is Map<String, dynamic>) {
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
return <String, dynamic>{'raw': trimmed};
|
|
||||||
}
|
|
||||||
return const <String, dynamic>{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HotgameJsBridge {
|
|
||||||
static const String channelName = 'HotgameBridgeChannel';
|
|
||||||
|
|
||||||
static String bootstrapScript({Map<String, dynamic>? launchPayload}) {
|
|
||||||
final safeLaunchPayload = jsonEncode(
|
|
||||||
launchPayload ?? const <String, dynamic>{},
|
|
||||||
);
|
|
||||||
return '''
|
|
||||||
(function() {
|
|
||||||
const launchPayload = $safeLaunchPayload;
|
|
||||||
|
|
||||||
function toPayload(input) {
|
|
||||||
if (typeof input === 'string') {
|
|
||||||
try {
|
|
||||||
return JSON.parse(input);
|
|
||||||
} catch (_) {
|
|
||||||
return input.trim() ? { raw: input.trim() } : {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (input && typeof input === 'object') {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringifyForLog(value) {
|
|
||||||
if (value == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return value.length > 320 ? value.slice(0, 320) + '...' : value;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const text = JSON.stringify(value);
|
|
||||||
return text.length > 320 ? text.slice(0, 320) + '...' : text;
|
|
||||||
} catch (_) {
|
|
||||||
return String(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postMessage(action, input) {
|
|
||||||
const payload = toPayload(input);
|
|
||||||
$channelName.postMessage(JSON.stringify({
|
|
||||||
action: action,
|
|
||||||
payload: payload
|
|
||||||
}));
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
function postDebug(tag, payload) {
|
|
||||||
postMessage('${HotgameBridgeActions.debugLog}', {
|
|
||||||
tag: tag,
|
|
||||||
message: stringifyForLog(payload)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchLaunchPayload(payload) {
|
|
||||||
const normalizedPayload =
|
|
||||||
payload && typeof payload === 'object' ? payload : {};
|
|
||||||
const launchConfig =
|
|
||||||
normalizedPayload.launchConfig &&
|
|
||||||
typeof normalizedPayload.launchConfig === 'object'
|
|
||||||
? normalizedPayload.launchConfig
|
|
||||||
: {};
|
|
||||||
const entry =
|
|
||||||
normalizedPayload.entry && typeof normalizedPayload.entry === 'object'
|
|
||||||
? normalizedPayload.entry
|
|
||||||
: {};
|
|
||||||
window.__hotgameLaunchPayload = normalizedPayload;
|
|
||||||
window.hotgameLaunchPayload = normalizedPayload;
|
|
||||||
window.__hotgameLaunchConfig = launchConfig;
|
|
||||||
window.hotgameLaunchConfig = launchConfig;
|
|
||||||
window.__hotgameLaunchEntry = entry;
|
|
||||||
window.hotgameLaunchEntry = entry;
|
|
||||||
if (typeof window.onHotgameLaunchConfig === 'function') {
|
|
||||||
window.onHotgameLaunchConfig(launchConfig, normalizedPayload);
|
|
||||||
}
|
|
||||||
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent('hotgameLaunchConfig', { detail: launchConfig })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function installPostMessageObject(name, action) {
|
|
||||||
const existing = window[name];
|
|
||||||
if (!existing || typeof existing.postMessage !== 'function') {
|
|
||||||
window[name] = {
|
|
||||||
postMessage: function(body) {
|
|
||||||
return postMessage(action, body);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function installWebkitHandler(name, action) {
|
|
||||||
window.webkit = window.webkit || {};
|
|
||||||
window.webkit.messageHandlers = window.webkit.messageHandlers || {};
|
|
||||||
if (!window.webkit.messageHandlers[name] ||
|
|
||||||
typeof window.webkit.messageHandlers[name].postMessage !== 'function') {
|
|
||||||
window.webkit.messageHandlers[name] = {
|
|
||||||
postMessage: function(body) {
|
|
||||||
return postMessage(action, body);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function installJsBridgeFunction(name, action) {
|
|
||||||
window.JsBridge = window.JsBridge || {};
|
|
||||||
window.JsBridge[name] = function(body) {
|
|
||||||
return postMessage(action, body || {});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.__hotgameBridgeReady) {
|
|
||||||
window.__hotgameBridgeReady = true;
|
|
||||||
installJsBridgeFunction('recharge', '${HotgameBridgeActions.recharge}');
|
|
||||||
installJsBridgeFunction('quit', '${HotgameBridgeActions.quit}');
|
|
||||||
installJsBridgeFunction('exit', '${HotgameBridgeActions.quit}');
|
|
||||||
installJsBridgeFunction('closeGame', '${HotgameBridgeActions.quit}');
|
|
||||||
installPostMessageObject('recharge', '${HotgameBridgeActions.recharge}');
|
|
||||||
installPostMessageObject('quit', '${HotgameBridgeActions.quit}');
|
|
||||||
installPostMessageObject('loadComplete', '${HotgameBridgeActions.loadComplete}');
|
|
||||||
installWebkitHandler('recharge', '${HotgameBridgeActions.recharge}');
|
|
||||||
installWebkitHandler('quit', '${HotgameBridgeActions.quit}');
|
|
||||||
installWebkitHandler('loadComplete', '${HotgameBridgeActions.loadComplete}');
|
|
||||||
window.NativeBridge = window.NativeBridge || {};
|
|
||||||
window.NativeBridge.recharge = function(body) {
|
|
||||||
return postMessage('${HotgameBridgeActions.recharge}', body || {});
|
|
||||||
};
|
|
||||||
window.NativeBridge.quit = function(body) {
|
|
||||||
return postMessage('${HotgameBridgeActions.quit}', body || {});
|
|
||||||
};
|
|
||||||
window.NativeBridge.closeGame = function(body) {
|
|
||||||
return postMessage('${HotgameBridgeActions.quit}', body || {});
|
|
||||||
};
|
|
||||||
window.NativeBridge.rechargeSuccess = function(body) {
|
|
||||||
return notifyRechargeSuccess(body || {});
|
|
||||||
};
|
|
||||||
window.__hotgameBridgeDebug = postDebug;
|
|
||||||
postDebug('bridge.ready', {
|
|
||||||
href: window.location && window.location.href,
|
|
||||||
hasJsBridge: !!window.JsBridge,
|
|
||||||
hasRechargeSuccess: typeof window.rechargeSuccess === 'function'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function notifyRechargeSuccess(body) {
|
|
||||||
const payload = body || {};
|
|
||||||
let notified = false;
|
|
||||||
if (typeof window.rechargeSuccess === 'function') {
|
|
||||||
window.rechargeSuccess();
|
|
||||||
notified = true;
|
|
||||||
}
|
|
||||||
if (typeof window.onRechargeSuccess === 'function') {
|
|
||||||
window.onRechargeSuccess(payload);
|
|
||||||
notified = true;
|
|
||||||
}
|
|
||||||
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent('rechargeSuccess', { detail: payload })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return notified;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__hotgameNotifyRechargeSuccess = notifyRechargeSuccess;
|
|
||||||
dispatchLaunchPayload(launchPayload);
|
|
||||||
})();
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
|
|
||||||
static String buildRechargeSuccessScript({Map<String, dynamic>? payload}) {
|
|
||||||
final safePayload = jsonEncode(
|
|
||||||
payload ??
|
|
||||||
<String, dynamic>{'timestamp': DateTime.now().millisecondsSinceEpoch},
|
|
||||||
);
|
|
||||||
return '''
|
|
||||||
(function() {
|
|
||||||
const payload = $safePayload;
|
|
||||||
if (typeof window.__hotgameBridgeDebug === 'function') {
|
|
||||||
window.__hotgameBridgeDebug('recharge.success', payload);
|
|
||||||
}
|
|
||||||
if (typeof window.__hotgameNotifyRechargeSuccess === 'function') {
|
|
||||||
window.__hotgameNotifyRechargeSuccess(payload);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof window.rechargeSuccess === 'function') {
|
|
||||||
window.rechargeSuccess();
|
|
||||||
}
|
|
||||||
if (typeof window.onRechargeSuccess === 'function') {
|
|
||||||
window.onRechargeSuccess(payload);
|
|
||||||
}
|
|
||||||
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
|
|
||||||
window.dispatchEvent(
|
|
||||||
new CustomEvent('rechargeSuccess', { detail: payload })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -127,10 +127,6 @@ class RoomGameListItemModel {
|
|||||||
bool get isLeader =>
|
bool get isLeader =>
|
||||||
provider.toUpperCase() == 'LEADER' || gameType.toUpperCase() == 'LEADER';
|
provider.toUpperCase() == 'LEADER' || gameType.toUpperCase() == 'LEADER';
|
||||||
|
|
||||||
bool get isHotgame =>
|
|
||||||
provider.toUpperCase() == 'HOTGAME' ||
|
|
||||||
gameType.toUpperCase() == 'HOTGAME';
|
|
||||||
|
|
||||||
factory RoomGameListItemModel.fromJson(Map<String, dynamic> json) {
|
factory RoomGameListItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
return RoomGameListItemModel(
|
return RoomGameListItemModel(
|
||||||
gameId: _asString(json['gameId']),
|
gameId: _asString(json['gameId']),
|
||||||
|
|||||||
@ -6,24 +6,24 @@ class BaishunLoadingView extends StatelessWidget {
|
|||||||
const BaishunLoadingView({
|
const BaishunLoadingView({
|
||||||
super.key,
|
super.key,
|
||||||
this.message = 'Loading game...',
|
this.message = 'Loading game...',
|
||||||
this.backgroundColor = const Color(0xFF081915),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
final Color backgroundColor;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: backgroundColor,
|
color: Colors.black.withValues(alpha: 0.42),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 22.w, vertical: 18.w),
|
padding: EdgeInsets.symmetric(horizontal: 22.w, vertical: 18.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF102D28).withValues(alpha: 0.92),
|
color: const Color(0xFF102D28).withValues(alpha: 0.92),
|
||||||
borderRadius: BorderRadius.circular(16.w),
|
borderRadius: BorderRadius.circular(16.w),
|
||||||
border: Border.all(color: Colors.white.withValues(alpha: 0.1)),
|
border: Border.all(
|
||||||
|
color: Colors.white.withValues(alpha: 0.1),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|||||||
@ -1,640 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
|
||||||
import 'package:yumi/modules/room_game/bridge/hotgame_js_bridge.dart';
|
|
||||||
import 'package:yumi/modules/room_game/data/models/room_game_models.dart';
|
|
||||||
import 'package:yumi/modules/room_game/data/room_game_repository.dart';
|
|
||||||
import 'package:yumi/modules/room_game/utils/room_game_viewport.dart';
|
|
||||||
import 'package:yumi/modules/room_game/views/baishun_loading_view.dart';
|
|
||||||
import 'package:yumi/modules/wallet/wallet_route.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_h5_url_utils.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
|
||||||
|
|
||||||
class HotgameGamePage extends StatefulWidget {
|
|
||||||
const HotgameGamePage({
|
|
||||||
super.key,
|
|
||||||
required this.roomId,
|
|
||||||
required this.game,
|
|
||||||
required this.launchModel,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String roomId;
|
|
||||||
final RoomGameListItemModel game;
|
|
||||||
final BaishunLaunchModel launchModel;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<HotgameGamePage> createState() => _HotgameGamePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HotgameGamePageState extends State<HotgameGamePage> {
|
|
||||||
static const double _sevenScreenRatio = 0.72;
|
|
||||||
|
|
||||||
final RoomGameRepository _repository = RoomGameRepository();
|
|
||||||
final Set<Factory<OneSequenceGestureRecognizer>> _webGestureRecognizers =
|
|
||||||
<Factory<OneSequenceGestureRecognizer>>{
|
|
||||||
Factory<OneSequenceGestureRecognizer>(() => EagerGestureRecognizer()),
|
|
||||||
};
|
|
||||||
late final WebViewController _controller;
|
|
||||||
|
|
||||||
Timer? _bridgeBootstrapTimer;
|
|
||||||
Timer? _loadingFallbackTimer;
|
|
||||||
|
|
||||||
bool _isLoading = true;
|
|
||||||
bool _isClosing = false;
|
|
||||||
bool _didReceiveBridgeMessage = false;
|
|
||||||
bool _didFinishPageLoad = false;
|
|
||||||
bool _didInjectProgressBridge = false;
|
|
||||||
int _bridgeInjectCount = 0;
|
|
||||||
String? _errorMessage;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller =
|
|
||||||
WebViewController()
|
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
|
||||||
..setBackgroundColor(Colors.black)
|
|
||||||
..addJavaScriptChannel(
|
|
||||||
HotgameJsBridge.channelName,
|
|
||||||
onMessageReceived: _handleBridgeMessage,
|
|
||||||
)
|
|
||||||
..addJavaScriptChannel(
|
|
||||||
HotgameBridgeActions.recharge,
|
|
||||||
onMessageReceived:
|
|
||||||
(JavaScriptMessage message) => _handleNamedChannelMessage(
|
|
||||||
HotgameBridgeActions.recharge,
|
|
||||||
message,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
..addJavaScriptChannel(
|
|
||||||
HotgameBridgeActions.quit,
|
|
||||||
onMessageReceived:
|
|
||||||
(JavaScriptMessage message) => _handleNamedChannelMessage(
|
|
||||||
HotgameBridgeActions.quit,
|
|
||||||
message,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
..addJavaScriptChannel(
|
|
||||||
HotgameBridgeActions.loadComplete,
|
|
||||||
onMessageReceived:
|
|
||||||
(JavaScriptMessage message) => _handleNamedChannelMessage(
|
|
||||||
HotgameBridgeActions.loadComplete,
|
|
||||||
message,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
..setNavigationDelegate(
|
|
||||||
NavigationDelegate(
|
|
||||||
onPageStarted: (String url) {
|
|
||||||
_log('page_started url=${_clip(url, 240)}');
|
|
||||||
_prepareForPageLoad();
|
|
||||||
},
|
|
||||||
onPageFinished: (String url) async {
|
|
||||||
_didFinishPageLoad = true;
|
|
||||||
_log('page_finished url=${_clip(url, 240)}');
|
|
||||||
await _injectBridge(reason: 'page_finished');
|
|
||||||
if (!mounted || _errorMessage != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onProgress: (int progress) {
|
|
||||||
if (_didInjectProgressBridge ||
|
|
||||||
progress < 10 ||
|
|
||||||
_errorMessage != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_didInjectProgressBridge = true;
|
|
||||||
unawaited(_injectBridge(reason: 'progress_$progress'));
|
|
||||||
},
|
|
||||||
onWebResourceError: (WebResourceError error) {
|
|
||||||
_log(
|
|
||||||
'web_resource_error code=${error.errorCode} '
|
|
||||||
'type=${error.errorType} desc=${_clip(error.description, 300)}',
|
|
||||||
);
|
|
||||||
_stopBridgeBootstrap(reason: 'web_resource_error');
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = error.description;
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_log('init launch=${_stringifyForLog(_buildLaunchSummary())}');
|
|
||||||
unawaited(_loadGameEntry());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_stopBridgeBootstrap(reason: 'dispose');
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _prepareForPageLoad() {
|
|
||||||
_didReceiveBridgeMessage = false;
|
|
||||||
_didFinishPageLoad = false;
|
|
||||||
_didInjectProgressBridge = false;
|
|
||||||
_bridgeInjectCount = 0;
|
|
||||||
_stopBridgeBootstrap(reason: 'prepare_page_load');
|
|
||||||
_bridgeBootstrapTimer = Timer.periodic(const Duration(milliseconds: 250), (
|
|
||||||
Timer timer,
|
|
||||||
) {
|
|
||||||
if (!mounted || _didReceiveBridgeMessage || _errorMessage != null) {
|
|
||||||
timer.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unawaited(_injectBridge(reason: 'bootstrap'));
|
|
||||||
if (_didFinishPageLoad && timer.tick >= 12) {
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_loadingFallbackTimer = Timer(const Duration(seconds: 6), () {
|
|
||||||
if (!mounted || _didReceiveBridgeMessage || _errorMessage != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_isLoading = true;
|
|
||||||
_errorMessage = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stopBridgeBootstrap({String reason = 'manual'}) {
|
|
||||||
if (_bridgeBootstrapTimer != null || _loadingFallbackTimer != null) {
|
|
||||||
_log('stop_bootstrap reason=$reason');
|
|
||||||
}
|
|
||||||
_bridgeBootstrapTimer?.cancel();
|
|
||||||
_bridgeBootstrapTimer = null;
|
|
||||||
_loadingFallbackTimer?.cancel();
|
|
||||||
_loadingFallbackTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadGameEntry() async {
|
|
||||||
try {
|
|
||||||
_prepareForPageLoad();
|
|
||||||
final entryUrl = widget.launchModel.entry.entryUrl.trim();
|
|
||||||
_log(
|
|
||||||
'load_entry launchMode=${widget.launchModel.entry.launchMode} '
|
|
||||||
'entryUrl=${_clip(entryUrl, 240)}',
|
|
||||||
);
|
|
||||||
if (entryUrl.isEmpty || entryUrl.startsWith('mock://')) {
|
|
||||||
await _controller.loadHtmlString(_buildMockHtml());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final h5Url = SCH5UrlUtils.appendToken(entryUrl);
|
|
||||||
final uri = Uri.tryParse(h5Url);
|
|
||||||
if (uri == null) {
|
|
||||||
throw Exception('Invalid hotgame entry url: $entryUrl');
|
|
||||||
}
|
|
||||||
await _controller.loadRequest(uri);
|
|
||||||
} catch (error) {
|
|
||||||
_log('load_entry_error error=${_clip(error.toString(), 400)}');
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = error.toString();
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _injectBridge({String reason = 'manual'}) async {
|
|
||||||
_bridgeInjectCount += 1;
|
|
||||||
if (reason != 'bootstrap' ||
|
|
||||||
_bridgeInjectCount <= 3 ||
|
|
||||||
_bridgeInjectCount % 5 == 0) {
|
|
||||||
_log('inject_bridge reason=$reason count=$_bridgeInjectCount');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await _controller.runJavaScript(
|
|
||||||
HotgameJsBridge.bootstrapScript(launchPayload: _buildLaunchPayload()),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
_log(
|
|
||||||
'inject_bridge_error reason=$reason error=${_clip(error.toString(), 300)}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _handleBridgeMessage(JavaScriptMessage message) async {
|
|
||||||
_log('channel_message raw=${_clip(message.message, 600)}');
|
|
||||||
final bridgeMessage = HotgameBridgeMessage.parse(message.message);
|
|
||||||
await _dispatchBridgeMessage(bridgeMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _handleNamedChannelMessage(
|
|
||||||
String action,
|
|
||||||
JavaScriptMessage message,
|
|
||||||
) async {
|
|
||||||
final bridgeMessage = HotgameBridgeMessage.fromAction(
|
|
||||||
action,
|
|
||||||
message.message,
|
|
||||||
);
|
|
||||||
_log('named_channel action=$action raw=${_clip(message.message, 600)}');
|
|
||||||
await _dispatchBridgeMessage(bridgeMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _dispatchBridgeMessage(
|
|
||||||
HotgameBridgeMessage bridgeMessage,
|
|
||||||
) async {
|
|
||||||
if (bridgeMessage.action == HotgameBridgeActions.debugLog) {
|
|
||||||
final tag = bridgeMessage.payload['tag']?.toString().trim();
|
|
||||||
final message = bridgeMessage.payload['message']?.toString() ?? '';
|
|
||||||
_log('h5_debug tag=${tag ?? 'unknown'} message=${_clip(message, 800)}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_didReceiveBridgeMessage = true;
|
|
||||||
_stopBridgeBootstrap(reason: 'bridge_message_${bridgeMessage.action}');
|
|
||||||
_log(
|
|
||||||
'bridge_action action=${bridgeMessage.action} '
|
|
||||||
'payload=${_stringifyForLog(_sanitizeForLog(bridgeMessage.payload))}',
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (bridgeMessage.action) {
|
|
||||||
case HotgameBridgeActions.recharge:
|
|
||||||
await _openRechargeAndNotifyGame();
|
|
||||||
break;
|
|
||||||
case HotgameBridgeActions.quit:
|
|
||||||
await _closeAndExit(reason: 'h5_quit');
|
|
||||||
break;
|
|
||||||
case HotgameBridgeActions.loadComplete:
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
_errorMessage = null;
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_log('bridge_action_unhandled action=${bridgeMessage.action}');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _openRechargeAndNotifyGame() async {
|
|
||||||
_log('hotgame_recharge open_wallet');
|
|
||||||
await SCNavigatorUtils.push(context, WalletRoute.recharge);
|
|
||||||
try {
|
|
||||||
await _controller.runJavaScript(
|
|
||||||
HotgameJsBridge.buildRechargeSuccessScript(),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
_log(
|
|
||||||
'recharge_success_notify_error error=${_clip(error.toString(), 300)}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _reload() async {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_errorMessage = null;
|
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
await _loadGameEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _closeAndExit({String reason = 'user_exit'}) async {
|
|
||||||
if (_isClosing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_isClosing = true;
|
|
||||||
try {
|
|
||||||
final sessionId = widget.launchModel.gameSessionId.trim();
|
|
||||||
if (sessionId.isNotEmpty && !sessionId.startsWith('mock')) {
|
|
||||||
await _repository.closeGame(
|
|
||||||
provider: widget.game.provider,
|
|
||||||
roomId: widget.roomId,
|
|
||||||
gameSessionId: sessionId,
|
|
||||||
reason: reason,
|
|
||||||
params: const <String, dynamic>{},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
_log('close_error error=${_clip(error.toString(), 300)}');
|
|
||||||
}
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _resolveScreenMode() {
|
|
||||||
return resolveRoomGameScreenMode(
|
|
||||||
widget.game.launchParams,
|
|
||||||
fullScreen: widget.game.fullScreen,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _resolveSafeHeight() {
|
|
||||||
if (widget.launchModel.entry.safeHeight > 0) {
|
|
||||||
return widget.launchModel.entry.safeHeight;
|
|
||||||
}
|
|
||||||
if (widget.game.safeHeight > 0) {
|
|
||||||
return widget.game.safeHeight;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double _calculateBodyHeight(BuildContext context) {
|
|
||||||
final screenMode = _resolveScreenMode();
|
|
||||||
final maxDialogHeight = resolveRoomGameAvailableHeight(
|
|
||||||
context,
|
|
||||||
reservedTop: 12.w,
|
|
||||||
);
|
|
||||||
if (maxDialogHeight <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (screenMode == 'full') {
|
|
||||||
return maxDialogHeight;
|
|
||||||
}
|
|
||||||
if (screenMode == 'seven') {
|
|
||||||
return maxDialogHeight * _sevenScreenRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
return clampRoomGameHeight(
|
|
||||||
_calculatePreferredBodyHeight(context),
|
|
||||||
maxDialogHeight,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _calculatePreferredBodyHeight(BuildContext context) {
|
|
||||||
final mediaQuery = MediaQuery.maybeOf(context);
|
|
||||||
final screenSize = mediaQuery?.size ?? Size.zero;
|
|
||||||
final safeHeight = _resolveSafeHeight();
|
|
||||||
final fallback = screenSize.width;
|
|
||||||
|
|
||||||
if (safeHeight > 0 && screenSize.width > 0) {
|
|
||||||
final ratio = roomGameDesignWidth / safeHeight;
|
|
||||||
return screenSize.width / ratio;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _log(String message) {}
|
|
||||||
|
|
||||||
String _maskValue(String value, {int keepStart = 6, int keepEnd = 4}) {
|
|
||||||
final trimmed = value.trim();
|
|
||||||
if (trimmed.isEmpty) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (trimmed.length <= keepStart + keepEnd) {
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
return '${trimmed.substring(0, keepStart)}***${trimmed.substring(trimmed.length - keepEnd)}';
|
|
||||||
}
|
|
||||||
|
|
||||||
Object? _sanitizeForLog(Object? value, {String key = ''}) {
|
|
||||||
final lowerKey = key.toLowerCase();
|
|
||||||
final shouldMask =
|
|
||||||
lowerKey.contains('code') ||
|
|
||||||
lowerKey.contains('token') ||
|
|
||||||
lowerKey.contains('authorization');
|
|
||||||
if (value is Map) {
|
|
||||||
final result = <String, dynamic>{};
|
|
||||||
for (final MapEntry<dynamic, dynamic> entry in value.entries) {
|
|
||||||
result[entry.key.toString()] = _sanitizeForLog(
|
|
||||||
entry.value,
|
|
||||||
key: entry.key.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (value is List) {
|
|
||||||
return value.map((Object? item) => _sanitizeForLog(item)).toList();
|
|
||||||
}
|
|
||||||
if (value is String) {
|
|
||||||
final normalized = shouldMask ? _maskValue(value) : value;
|
|
||||||
return _clip(normalized, 600);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _clip(String value, [int limit = 600]) {
|
|
||||||
final trimmed = value.trim();
|
|
||||||
if (trimmed.length <= limit) {
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
return '${trimmed.substring(0, limit)}...';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _stringifyForLog(Object? value) {
|
|
||||||
if (value == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return _clip(jsonEncode(value), 800);
|
|
||||||
} catch (_) {
|
|
||||||
return _clip(value.toString(), 800);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _buildLaunchPayload() {
|
|
||||||
return <String, dynamic>{
|
|
||||||
'provider': widget.launchModel.provider,
|
|
||||||
'gameId': widget.launchModel.gameId,
|
|
||||||
'providerGameId': widget.launchModel.providerGameId,
|
|
||||||
'gameSessionId': widget.launchModel.gameSessionId,
|
|
||||||
'entry': widget.launchModel.entry.toJson(),
|
|
||||||
'launchConfig': widget.launchModel.launchConfig.toJson(),
|
|
||||||
'roomState': widget.launchModel.roomState.toJson(),
|
|
||||||
'game': <String, dynamic>{
|
|
||||||
'gameId': widget.game.gameId,
|
|
||||||
'provider': widget.game.provider,
|
|
||||||
'gameType': widget.game.gameType,
|
|
||||||
'name': widget.game.name,
|
|
||||||
'launchMode': widget.game.launchMode,
|
|
||||||
'launchParams': widget.game.launchParams,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> _buildLaunchSummary() {
|
|
||||||
final payload = _buildLaunchPayload();
|
|
||||||
return <String, dynamic>{
|
|
||||||
'roomId': widget.roomId,
|
|
||||||
'provider': widget.game.provider,
|
|
||||||
'gameId': widget.game.gameId,
|
|
||||||
'providerGameId': widget.launchModel.providerGameId,
|
|
||||||
'gameSessionId': widget.launchModel.gameSessionId,
|
|
||||||
'entry': _sanitizeForLog(payload['entry']),
|
|
||||||
'launchConfig': _sanitizeForLog(payload['launchConfig']),
|
|
||||||
'roomState': _sanitizeForLog(payload['roomState']),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
String _buildMockHtml() {
|
|
||||||
return '''
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<style>
|
|
||||||
body { margin: 0; font-family: sans-serif; background: #111827; color: #fff; }
|
|
||||||
.wrap { padding: 24px; }
|
|
||||||
button {
|
|
||||||
margin: 8px 8px 0 0;
|
|
||||||
padding: 10px 16px;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #2563eb;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
#log { margin-top: 16px; white-space: pre-wrap; color: #bfdbfe; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrap">
|
|
||||||
<h3>Hotgame Mock</h3>
|
|
||||||
<button onclick="loadComplete.postMessage(null)">loadComplete</button>
|
|
||||||
<button onclick="window.JsBridge.recharge()">JsBridge.recharge()</button>
|
|
||||||
<button onclick="window.JsBridge.quit()">JsBridge.quit()</button>
|
|
||||||
<button onclick="window.webkit.messageHandlers.recharge.postMessage(null)">webkit recharge</button>
|
|
||||||
<button onclick="window.webkit.messageHandlers.quit.postMessage(null)">webkit quit</button>
|
|
||||||
<button onclick="window.rechargeSuccess && window.rechargeSuccess()">rechargeSuccess()</button>
|
|
||||||
<div id="log"></div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
window.rechargeSuccess = function() {
|
|
||||||
document.getElementById('log').textContent += '\\nrechargeSuccess called';
|
|
||||||
};
|
|
||||||
document.getElementById('log').textContent =
|
|
||||||
'launchConfig=' + JSON.stringify(window.hotgameLaunchConfig || {});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final bodyHeight = _calculateBodyHeight(context);
|
|
||||||
final bottomFrameHeight = resolveRoomGameCompatibilityBottomHeight(context);
|
|
||||||
return PopScope(
|
|
||||||
canPop: false,
|
|
||||||
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
|
||||||
if (didPop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _closeAndExit();
|
|
||||||
},
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(24.w),
|
|
||||||
topRight: Radius.circular(24.w),
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
width: ScreenUtil().screenWidth,
|
|
||||||
height: bodyHeight + bottomFrameHeight,
|
|
||||||
color: const Color(0xFF081915),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: bottomFrameHeight,
|
|
||||||
child: WebViewWidget(
|
|
||||||
controller: _controller,
|
|
||||||
gestureRecognizers: _webGestureRecognizers,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (bottomFrameHeight > 0)
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
height: bottomFrameHeight,
|
|
||||||
child: RoomGameCompatibilityBottomFrame(
|
|
||||||
height: bottomFrameHeight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_errorMessage != null) _buildErrorState(),
|
|
||||||
if (_isLoading && _errorMessage == null)
|
|
||||||
const IgnorePointer(
|
|
||||||
ignoring: true,
|
|
||||||
child: BaishunLoadingView(
|
|
||||||
message: 'Waiting for Hotgame...',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildErrorState() {
|
|
||||||
return Positioned.fill(
|
|
||||||
child: Container(
|
|
||||||
color: Colors.black.withValues(alpha: 0.88),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 28.w),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.error_outline, color: Colors.white70, size: 34.w),
|
|
||||||
SizedBox(height: 12.w),
|
|
||||||
Text(
|
|
||||||
'Hotgame failed to load',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 16.sp,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 8.w),
|
|
||||||
Text(
|
|
||||||
_errorMessage ?? '',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Colors.white70, fontSize: 12.sp),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16.w),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
TextButton(onPressed: _reload, child: const Text('Retry')),
|
|
||||||
SizedBox(width: 10.w),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
SCTts.show('Please verify the Hotgame entry url');
|
|
||||||
},
|
|
||||||
child: const Text('Tips'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,11 +9,9 @@ import 'package:yumi/modules/room_game/data/models/room_game_models.dart';
|
|||||||
import 'package:yumi/modules/room_game/data/room_game_repository.dart';
|
import 'package:yumi/modules/room_game/data/room_game_repository.dart';
|
||||||
import 'package:yumi/modules/room_game/utils/room_game_viewport.dart';
|
import 'package:yumi/modules/room_game/utils/room_game_viewport.dart';
|
||||||
import 'package:yumi/modules/room_game/views/baishun_game_page.dart';
|
import 'package:yumi/modules/room_game/views/baishun_game_page.dart';
|
||||||
import 'package:yumi/modules/room_game/views/hotgame_game_page.dart';
|
|
||||||
import 'package:yumi/modules/room_game/views/leader_game_page.dart';
|
import 'package:yumi/modules/room_game/views/leader_game_page.dart';
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/custom_cached_image.dart';
|
import 'package:yumi/ui_kit/components/custom_cached_image.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
@ -33,6 +31,7 @@ class RoomGameListSheet extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
||||||
|
static const String _logPrefix = '[RoomGameLaunch]';
|
||||||
static const int _itemsPerRow = 4;
|
static const int _itemsPerRow = 4;
|
||||||
static const String _sheetFrameAsset =
|
static const String _sheetFrameAsset =
|
||||||
'sc_images/room/sc_room_game_sheet_frame.png';
|
'sc_images/room/sc_room_game_sheet_frame.png';
|
||||||
@ -91,9 +90,7 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
|||||||
String targetGameId,
|
String targetGameId,
|
||||||
) {
|
) {
|
||||||
final supportedItems =
|
final supportedItems =
|
||||||
items
|
items.where((item) => item.isBaishun || item.isLeader).toList();
|
||||||
.where((item) => item.isBaishun || item.isLeader || item.isHotgame)
|
|
||||||
.toList();
|
|
||||||
final candidates = supportedItems.isNotEmpty ? supportedItems : items;
|
final candidates = supportedItems.isNotEmpty ? supportedItems : items;
|
||||||
if (targetGameId == '0') {
|
if (targetGameId == '0') {
|
||||||
return candidates[math.Random().nextInt(candidates.length)];
|
return candidates[math.Random().nextInt(candidates.length)];
|
||||||
@ -120,7 +117,7 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
|||||||
SCTts.show('roomId is empty');
|
SCTts.show('roomId is empty');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!game.isBaishun && !game.isLeader && !game.isHotgame) {
|
if (!game.isBaishun && !game.isLeader) {
|
||||||
SCTts.show('This game provider is not wired yet');
|
SCTts.show('This game provider is not wired yet');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -129,7 +126,6 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
|||||||
_launchingGameId = game.gameId;
|
_launchingGameId = game.gameId;
|
||||||
});
|
});
|
||||||
|
|
||||||
SCRoomTopLayerGuardLease? gameDialogGuard;
|
|
||||||
try {
|
try {
|
||||||
_log(
|
_log(
|
||||||
'launch_request roomId=$_roomId gameId=${game.gameId} '
|
'launch_request roomId=$_roomId gameId=${game.gameId} '
|
||||||
@ -151,38 +147,23 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameDialogGuard = SCRoomTopLayerGuard().acquire(reason: 'room_game_page');
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 180));
|
await Future<void>.delayed(const Duration(milliseconds: 180));
|
||||||
if (!widget.roomContext.mounted) {
|
if (!widget.roomContext.mounted) {
|
||||||
gameDialogGuard.release();
|
|
||||||
gameDialogGuard = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final gamePage = _buildGamePage(game, launchModel);
|
final gamePage = _buildGamePage(game, launchModel);
|
||||||
if (gamePage == null) {
|
if (gamePage == null) {
|
||||||
gameDialogGuard.release();
|
|
||||||
gameDialogGuard = null;
|
|
||||||
SCTts.show('This game provider is not wired yet');
|
SCTts.show('This game provider is not wired yet');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final activeGameDialogGuard = gameDialogGuard;
|
|
||||||
gameDialogGuard = null;
|
|
||||||
try {
|
|
||||||
unawaited(
|
|
||||||
showBottomInBottomDialog(
|
showBottomInBottomDialog(
|
||||||
widget.roomContext,
|
widget.roomContext,
|
||||||
gamePage,
|
gamePage,
|
||||||
barrierColor: Colors.black54,
|
barrierColor: Colors.black54,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
).whenComplete(activeGameDialogGuard.release),
|
|
||||||
);
|
);
|
||||||
} catch (_) {
|
|
||||||
activeGameDialogGuard.release();
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
gameDialogGuard?.release();
|
|
||||||
_log('launch_error error=${_clip(error.toString(), 400)}');
|
_log('launch_error error=${_clip(error.toString(), 400)}');
|
||||||
SCTts.show('Launch failed');
|
SCTts.show('Launch failed');
|
||||||
} finally {
|
} finally {
|
||||||
@ -270,13 +251,6 @@ class _RoomGameListSheetState extends State<RoomGameListSheet> {
|
|||||||
launchModel: launchModel,
|
launchModel: launchModel,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (game.isHotgame) {
|
|
||||||
return HotgameGamePage(
|
|
||||||
roomId: _roomId,
|
|
||||||
game: game,
|
|
||||||
launchModel: launchModel,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -178,8 +178,7 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
Image.asset(
|
Image.asset(
|
||||||
SCGlobalConfig.businessLogicStrategy.getSplashPageIcon(),
|
SCGlobalConfig.businessLogicStrategy.getSplashPageIcon(),
|
||||||
width: 107.w,
|
width: 107.w,
|
||||||
height: 97.w,
|
height: 159.w,
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
@ -18,11 +17,8 @@ import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
import 'package:yumi/modules/country/country_route.dart';
|
|
||||||
import '../../../shared/tools/sc_pick_utils.dart';
|
import '../../../shared/tools/sc_pick_utils.dart';
|
||||||
import '../../../shared/business_logic/models/res/country_res.dart';
|
|
||||||
import '../../../shared/business_logic/models/res/login_res.dart';
|
import '../../../shared/business_logic/models/res/login_res.dart';
|
||||||
import '../../../shared/business_logic/models/res/sc_user_identity_res.dart';
|
|
||||||
import '../../../shared/business_logic/usecases/sc_accurate_length_limiting_textInput_formatter.dart';
|
import '../../../shared/business_logic/usecases/sc_accurate_length_limiting_textInput_formatter.dart';
|
||||||
|
|
||||||
class EditUserInfoPage2 extends StatefulWidget {
|
class EditUserInfoPage2 extends StatefulWidget {
|
||||||
@ -41,9 +37,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
String? bornMonth;
|
String? bornMonth;
|
||||||
String? autograph;
|
String? autograph;
|
||||||
String? hobby;
|
String? hobby;
|
||||||
String? countryId;
|
|
||||||
String? countryCode;
|
|
||||||
String? countryName;
|
|
||||||
String? bornDay;
|
String? bornDay;
|
||||||
String? bornYear;
|
String? bornYear;
|
||||||
String nickName = "";
|
String nickName = "";
|
||||||
@ -60,32 +53,12 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
nickName = currentProfile?.userNickname ?? "";
|
nickName = currentProfile?.userNickname ?? "";
|
||||||
autograph = currentProfile?.autograph ?? "";
|
autograph = currentProfile?.autograph ?? "";
|
||||||
hobby = currentProfile?.hobby ?? "";
|
hobby = currentProfile?.hobby ?? "";
|
||||||
countryId = currentProfile?.countryId ?? "";
|
birthdayDate = _birthdayFromProfile(currentProfile);
|
||||||
countryCode = currentProfile?.countryCode ?? "";
|
bornMonth = _formatTwoDigits(currentProfile?.bornMonth);
|
||||||
countryName = currentProfile?.countryName ?? "";
|
bornDay = _formatTwoDigits(currentProfile?.bornDay);
|
||||||
birthdayDate =
|
bornYear = _formatYear(currentProfile?.bornYear);
|
||||||
_birthdayFromProfile(currentProfile) ??
|
|
||||||
_birthdayFromAge(currentProfile?.age);
|
|
||||||
bornMonth = _formatTwoDigits(
|
|
||||||
currentProfile?.bornMonth ?? birthdayDate?.month,
|
|
||||||
);
|
|
||||||
bornDay = _formatTwoDigits(currentProfile?.bornDay ?? birthdayDate?.day);
|
|
||||||
bornYear = _formatYear(currentProfile?.bornYear ?? birthdayDate?.year);
|
|
||||||
age = currentProfile?.age ?? _ageFromBirthday(birthdayDate);
|
age = currentProfile?.age ?? _ageFromBirthday(birthdayDate);
|
||||||
sex = currentProfile?.userSex;
|
sex = currentProfile?.userSex;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Provider.of<SocialChatUserProfileManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).getUserIdentity();
|
|
||||||
Provider.of<SCAppGeneralManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).fetchCountryList();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _preferNonEmpty(String? primary, String? fallback) {
|
String? _preferNonEmpty(String? primary, String? fallback) {
|
||||||
@ -98,6 +71,21 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
return primary ?? fallback;
|
return primary ?? fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isBrokenLocalMediaUrl(String? url) {
|
||||||
|
return (url ?? "").contains("/external/oss/local/");
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _preferUsableAvatar(String? primary, String? fallback) {
|
||||||
|
if ((primary ?? "").trim().isNotEmpty && !_isBrokenLocalMediaUrl(primary)) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
if ((fallback ?? "").trim().isNotEmpty &&
|
||||||
|
!_isBrokenLocalMediaUrl(fallback)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return _preferNonEmpty(primary, fallback);
|
||||||
|
}
|
||||||
|
|
||||||
DateTime? _birthdayFromProfile([SocialChatUserProfile? profile]) {
|
DateTime? _birthdayFromProfile([SocialChatUserProfile? profile]) {
|
||||||
final targetProfile =
|
final targetProfile =
|
||||||
profile ?? AccountStorage().getCurrentUser()?.userProfile;
|
profile ?? AccountStorage().getCurrentUser()?.userProfile;
|
||||||
@ -137,15 +125,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
return (DateTime.now().year - date.year).abs();
|
return (DateTime.now().year - date.year).abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime? _birthdayFromAge(num? age) {
|
|
||||||
final ageValue = age?.toInt() ?? 0;
|
|
||||||
if (ageValue <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final now = DateTime.now();
|
|
||||||
return DateTime(now.year - ageValue, now.month, now.day);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _birthdayDisplayText() {
|
String _birthdayDisplayText() {
|
||||||
if ((bornYear ?? "").isEmpty ||
|
if ((bornYear ?? "").isEmpty ||
|
||||||
(bornMonth ?? "").isEmpty ||
|
(bornMonth ?? "").isEmpty ||
|
||||||
@ -165,50 +144,16 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
return values.any((value) => !identical(value, _noChange));
|
return values.any((value) => !identical(value, _noChange));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _hasIdentity(SCUserIdentityRes? identity) {
|
|
||||||
if (identity == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return (identity.anchor ?? false) ||
|
|
||||||
(identity.bd ?? false) ||
|
|
||||||
(identity.agent ?? false) ||
|
|
||||||
(identity.bdLeader ?? false) ||
|
|
||||||
(identity.freightAgent ?? false) ||
|
|
||||||
(identity.superFreightAgent ?? false) ||
|
|
||||||
(identity.admin ?? false) ||
|
|
||||||
(identity.superAdmin ?? false) ||
|
|
||||||
(identity.manager ?? false) ||
|
|
||||||
(identity.yumiManager ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _canChangeCountry(SCUserIdentityRes? identity) {
|
|
||||||
return !_hasIdentity(identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _countryDisplayText() {
|
|
||||||
final displayName = (countryName ?? "").trim();
|
|
||||||
if (displayName.isNotEmpty) {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
return (countryCode ?? "").trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _syncLocalProfileState(SocialChatUserProfile profile) {
|
void _syncLocalProfileState(SocialChatUserProfile profile) {
|
||||||
userCover = profile.userAvatar;
|
userCover = _preferUsableAvatar(profile.userAvatar, userCover);
|
||||||
nickName = profile.userNickname ?? nickName;
|
nickName = profile.userNickname ?? nickName;
|
||||||
autograph = profile.autograph ?? autograph;
|
autograph = profile.autograph ?? autograph;
|
||||||
hobby = profile.hobby ?? hobby;
|
hobby = profile.hobby ?? hobby;
|
||||||
countryId = profile.countryId ?? countryId;
|
|
||||||
countryCode = profile.countryCode ?? countryCode;
|
|
||||||
countryName = profile.countryName ?? countryName;
|
|
||||||
sex = profile.userSex ?? sex;
|
sex = profile.userSex ?? sex;
|
||||||
birthdayDate =
|
birthdayDate = _birthdayFromProfile(profile) ?? birthdayDate;
|
||||||
_birthdayFromProfile(profile) ??
|
bornYear = _formatYear(profile.bornYear);
|
||||||
_birthdayFromAge(profile.age) ??
|
bornMonth = _formatTwoDigits(profile.bornMonth);
|
||||||
birthdayDate;
|
bornDay = _formatTwoDigits(profile.bornDay);
|
||||||
bornYear = _formatYear(profile.bornYear ?? birthdayDate?.year);
|
|
||||||
bornMonth = _formatTwoDigits(profile.bornMonth ?? birthdayDate?.month);
|
|
||||||
bornDay = _formatTwoDigits(profile.bornDay ?? birthdayDate?.day);
|
|
||||||
age = profile.age ?? _ageFromBirthday(birthdayDate) ?? age;
|
age = profile.age ?? _ageFromBirthday(birthdayDate) ?? age;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,9 +223,11 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
String url,
|
String url,
|
||||||
) {
|
) {
|
||||||
if (success) {
|
if (success) {
|
||||||
submit(userAvatarValue: url);
|
userCover = url;
|
||||||
|
setState(() {});
|
||||||
|
submitAvatarOnly();
|
||||||
}
|
}
|
||||||
}, allowGif: true);
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
@ -296,7 +243,9 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
sex == 1
|
sex == 1
|
||||||
? SCAppLocalizations.of(context)!.man
|
? SCAppLocalizations.of(context)!.man
|
||||||
: SCAppLocalizations.of(context)!.woman,
|
: SCAppLocalizations.of(context)!.woman,
|
||||||
null,
|
() {
|
||||||
|
_showSexDialog();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
_buildItem(
|
_buildItem(
|
||||||
@ -321,12 +270,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
_showInputBioHobby(hobby ?? "", 2);
|
_showInputBioHobby(hobby ?? "", 2);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (_canChangeCountry(ref.userIdentity))
|
|
||||||
_buildItem(
|
|
||||||
"${SCAppLocalizations.of(context)!.countryRegion}:",
|
|
||||||
_countryDisplayText(),
|
|
||||||
_openCountryPicker,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -447,6 +390,13 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
return eighteenYearsAgo;
|
return eighteenYearsAgo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void submitAvatarOnly() {
|
||||||
|
if ((userCover ?? "").trim().isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submit(userAvatarValue: userCover);
|
||||||
|
}
|
||||||
|
|
||||||
void submit({
|
void submit({
|
||||||
Object? userAvatarValue = _noChange,
|
Object? userAvatarValue = _noChange,
|
||||||
Object? userNicknameValue = _noChange,
|
Object? userNicknameValue = _noChange,
|
||||||
@ -457,7 +407,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
Object? bornDayValue = _noChange,
|
Object? bornDayValue = _noChange,
|
||||||
Object? hobbyValue = _noChange,
|
Object? hobbyValue = _noChange,
|
||||||
Object? autographValue = _noChange,
|
Object? autographValue = _noChange,
|
||||||
Object? countryValue = _noChange,
|
|
||||||
}) async {
|
}) async {
|
||||||
if (!_hasChanges([
|
if (!_hasChanges([
|
||||||
userAvatarValue,
|
userAvatarValue,
|
||||||
@ -469,7 +418,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
bornDayValue,
|
bornDayValue,
|
||||||
hobbyValue,
|
hobbyValue,
|
||||||
autographValue,
|
autographValue,
|
||||||
countryValue,
|
|
||||||
])) {
|
])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -484,8 +432,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
_isSubmitting = true;
|
_isSubmitting = true;
|
||||||
SCLoadingManager.show();
|
SCLoadingManager.show();
|
||||||
try {
|
try {
|
||||||
final selectedCountry =
|
|
||||||
identical(countryValue, _noChange) ? null : countryValue as Country?;
|
|
||||||
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
||||||
userAvatar:
|
userAvatar:
|
||||||
identical(userAvatarValue, _noChange)
|
identical(userAvatarValue, _noChange)
|
||||||
@ -511,9 +457,14 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
identical(autographValue, _noChange)
|
identical(autographValue, _noChange)
|
||||||
? null
|
? null
|
||||||
: autographValue as String?,
|
: autographValue as String?,
|
||||||
countryId: selectedCountry?.id,
|
|
||||||
);
|
);
|
||||||
final mergedProfile = updatedProfile.copyWith(
|
final mergedProfile = updatedProfile.copyWith(
|
||||||
|
userAvatar: _preferUsableAvatar(
|
||||||
|
updatedProfile.userAvatar,
|
||||||
|
identical(userAvatarValue, _noChange)
|
||||||
|
? userCover
|
||||||
|
: userAvatarValue as String?,
|
||||||
|
),
|
||||||
userNickname: _preferNonEmpty(
|
userNickname: _preferNonEmpty(
|
||||||
updatedProfile.userNickname,
|
updatedProfile.userNickname,
|
||||||
identical(userNicknameValue, _noChange)
|
identical(userNicknameValue, _noChange)
|
||||||
@ -551,21 +502,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
(identical(bornYearValue, _noChange)
|
(identical(bornYearValue, _noChange)
|
||||||
? birthdayDate?.year
|
? birthdayDate?.year
|
||||||
: bornYearValue as num?),
|
: bornYearValue as num?),
|
||||||
countryId: _preferNonEmpty(
|
|
||||||
updatedProfile.countryId,
|
|
||||||
selectedCountry?.id,
|
|
||||||
),
|
|
||||||
countryCode: _preferNonEmpty(
|
|
||||||
updatedProfile.countryCode,
|
|
||||||
selectedCountry?.alphaTwo,
|
|
||||||
),
|
|
||||||
countryName: _preferNonEmpty(
|
|
||||||
updatedProfile.countryName,
|
|
||||||
_preferNonEmpty(
|
|
||||||
selectedCountry?.countryName,
|
|
||||||
selectedCountry?.aliasName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
_syncLocalProfileState(mergedProfile);
|
_syncLocalProfileState(mergedProfile);
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
@ -579,50 +515,13 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
true;
|
true;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful);
|
SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful);
|
||||||
if (selectedCountry != null) {
|
} catch (e) {
|
||||||
AccountStorage().logout(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// The repository/network layer already shows request errors, so keep the
|
|
||||||
// current edit page state and only release the submit/loading guards here.
|
|
||||||
} finally {
|
} finally {
|
||||||
_isSubmitting = false;
|
_isSubmitting = false;
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openCountryPicker() async {
|
|
||||||
final userIdentity =
|
|
||||||
Provider.of<SocialChatUserProfileManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).userIdentity;
|
|
||||||
if (!_canChangeCountry(userIdentity)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final generalManager = Provider.of<SCAppGeneralManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
);
|
|
||||||
generalManager.clearCountrySelection();
|
|
||||||
await SCNavigatorUtils.push(context, CountryRoute.country, replace: false);
|
|
||||||
if (!mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final selectedCountry = generalManager.selectCountryInfo;
|
|
||||||
if (selectedCountry == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final selectedCountryId = (selectedCountry.id ?? "").trim();
|
|
||||||
if (selectedCountryId.isEmpty ||
|
|
||||||
selectedCountryId == (countryId ?? "").trim()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
submit(countryValue: selectedCountry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: unused_element
|
|
||||||
void _showSexDialog() {
|
void _showSexDialog() {
|
||||||
showCenterDialog(
|
showCenterDialog(
|
||||||
context,
|
context,
|
||||||
@ -704,7 +603,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildItem(String title, String value, Function()? func) {
|
_buildItem(String title, String value, Function() func) {
|
||||||
return SCDebounceWidget(
|
return SCDebounceWidget(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -723,7 +622,6 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
child: text(value, textColor: Colors.white, fontSize: 15.sp),
|
child: text(value, textColor: Colors.white, fontSize: 15.sp),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (func != null)
|
|
||||||
Icon(
|
Icon(
|
||||||
Icons.keyboard_arrow_right,
|
Icons.keyboard_arrow_right,
|
||||||
size: 20.w,
|
size: 20.w,
|
||||||
@ -742,7 +640,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
func?.call();
|
func.call();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
@ -19,26 +17,19 @@ import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_vip_repository_imp.dart';
|
||||||
import 'package:yumi/shared/tools/sc_lk_event_bus.dart';
|
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/id/sc_special_id_badge.dart';
|
import 'package:yumi/ui_kit/widgets/id/sc_special_id_badge.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
|
import 'package:yumi/ui_kit/widgets/svga/sc_svga_asset_widget.dart';
|
||||||
|
|
||||||
class MePage2 extends StatefulWidget {
|
class MePage2 extends StatefulWidget {
|
||||||
const MePage2({super.key, this.currentIndexListenable, this.tabIndex = 3});
|
const MePage2({super.key});
|
||||||
|
|
||||||
final ValueListenable<int>? currentIndexListenable;
|
|
||||||
final int tabIndex;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MePage2> createState() => _MePage2State();
|
State<MePage2> createState() => _MePage2State();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
class _MePage2State extends State<MePage2> {
|
||||||
static const Duration _counterStaleDuration = Duration(seconds: 30);
|
|
||||||
static const String _cpActivityUrl =
|
|
||||||
'https://h5.global-interaction.com/activity/cp-space/yumi.html';
|
|
||||||
static const String _inviteActivityUrl =
|
static const String _inviteActivityUrl =
|
||||||
'https://h5.haiyihy.com/app-invite/index.html';
|
'https://h5.haiyihy.com/app-invite/index.html';
|
||||||
static const double _inviteActivityHorizontalScale = 750 / 662;
|
static const double _inviteActivityHorizontalScale = 750 / 662;
|
||||||
@ -46,52 +37,15 @@ class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
|||||||
Map<String, SCUserCounterRes> _counterMap = {};
|
Map<String, SCUserCounterRes> _counterMap = {};
|
||||||
SCVipStatusRes? _vipStatus;
|
SCVipStatusRes? _vipStatus;
|
||||||
bool _isVipStatusLoading = false;
|
bool _isVipStatusLoading = false;
|
||||||
bool _isCounterLoading = false;
|
|
||||||
bool _counterRefreshQueued = false;
|
|
||||||
bool _counterReloadAfterCurrent = false;
|
|
||||||
DateTime? _lastCounterLoadedAt;
|
|
||||||
StreamSubscription<SCUserRelationChangedEvent>? _relationChangedSubscription;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
widget.currentIndexListenable?.addListener(_handleTabVisibilityChanged);
|
|
||||||
_relationChangedSubscription = eventBus
|
|
||||||
.on<SCUserRelationChangedEvent>()
|
|
||||||
.listen(_handleUserRelationChanged);
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_loadPageData();
|
_loadPageData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant MePage2 oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (oldWidget.currentIndexListenable == widget.currentIndexListenable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
oldWidget.currentIndexListenable?.removeListener(
|
|
||||||
_handleTabVisibilityChanged,
|
|
||||||
);
|
|
||||||
widget.currentIndexListenable?.addListener(_handleTabVisibilityChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
widget.currentIndexListenable?.removeListener(_handleTabVisibilityChanged);
|
|
||||||
_relationChangedSubscription?.cancel();
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
||||||
if (state == AppLifecycleState.resumed && _isCurrentTabVisible) {
|
|
||||||
_refreshCounterIfNeeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loadPageData() {
|
void _loadPageData() {
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
||||||
context,
|
context,
|
||||||
@ -104,89 +58,10 @@ class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
|||||||
_loadVipStatus();
|
_loadVipStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get _isCurrentTabVisible {
|
Future<void> _loadCounter() async {
|
||||||
final currentIndex = widget.currentIndexListenable?.value;
|
|
||||||
return currentIndex == null || currentIndex == widget.tabIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isCounterStale {
|
|
||||||
final lastLoadedAt = _lastCounterLoadedAt;
|
|
||||||
if (lastLoadedAt == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return DateTime.now().difference(lastLoadedAt) >= _counterStaleDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleTabVisibilityChanged() {
|
|
||||||
if (!_isCurrentTabVisible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_refreshCounterIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _refreshCounterIfNeeded() {
|
|
||||||
if (_counterRefreshQueued || _isCounterStale) {
|
|
||||||
unawaited(_loadCounter(force: true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleUserRelationChanged(SCUserRelationChangedEvent event) {
|
|
||||||
final currentUserId =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
|
||||||
if (currentUserId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final actorUserId = event.actorUserId.trim();
|
|
||||||
final targetUserId = event.targetUserId.trim();
|
|
||||||
final affectsCurrentUser =
|
|
||||||
actorUserId == currentUserId || targetUserId == currentUserId;
|
|
||||||
if (!affectsCurrentUser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final delta = event.isFollowing ? 1 : -1;
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
if (actorUserId == currentUserId) {
|
|
||||||
_applyCounterDelta("SUBSCRIPTION", delta);
|
|
||||||
}
|
|
||||||
if (targetUserId == currentUserId) {
|
|
||||||
_applyCounterDelta("FANS", delta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_counterRefreshQueued = true;
|
|
||||||
if (_isCurrentTabVisible) {
|
|
||||||
unawaited(_loadCounter(force: true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _applyCounterDelta(String type, int delta) {
|
|
||||||
final currentCounter = _counterMap[type];
|
|
||||||
final currentQuantity = int.tryParse(currentCounter?.quantity ?? "0") ?? 0;
|
|
||||||
final nextQuantity = (currentQuantity + delta).clamp(0, 1 << 31);
|
|
||||||
_counterMap = {
|
|
||||||
..._counterMap,
|
|
||||||
type: (currentCounter ?? SCUserCounterRes(counterType: type)).copyWith(
|
|
||||||
quantity: nextQuantity.toString(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadCounter({bool force = false}) async {
|
|
||||||
if (_isCounterLoading) {
|
|
||||||
if (force || _counterRefreshQueued) {
|
|
||||||
_counterReloadAfterCurrent = true;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!force && !_counterRefreshQueued && !_isCounterStale) return;
|
|
||||||
final userId = AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
|
final userId = AccountStorage().getCurrentUser()?.userProfile?.id ?? "";
|
||||||
if (userId.isEmpty) return;
|
if (userId.isEmpty) return;
|
||||||
|
|
||||||
_isCounterLoading = true;
|
|
||||||
try {
|
try {
|
||||||
final userCounterList = await SCAccountRepository().userCounter(userId);
|
final userCounterList = await SCAccountRepository().userCounter(userId);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -196,17 +71,8 @@ class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
|||||||
(counter) => MapEntry(counter.counterType ?? "", counter),
|
(counter) => MapEntry(counter.counterType ?? "", counter),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_lastCounterLoadedAt = DateTime.now();
|
|
||||||
_counterRefreshQueued = false;
|
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (_) {}
|
||||||
} finally {
|
|
||||||
_isCounterLoading = false;
|
|
||||||
if (_counterReloadAfterCurrent && mounted) {
|
|
||||||
_counterReloadAfterCurrent = false;
|
|
||||||
unawaited(_loadCounter(force: true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadVipStatus() async {
|
Future<void> _loadVipStatus() async {
|
||||||
@ -301,21 +167,6 @@ class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openCpActivity() {
|
|
||||||
final cpActivityUrl = _cpActivityUrl.trim();
|
|
||||||
if (cpActivityUrl.isEmpty) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint('[MePage2] cp activity url is empty');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SCNavigatorUtils.push(
|
|
||||||
context,
|
|
||||||
"${SCMainRoute.webViewPage}?url=${Uri.encodeComponent(cpActivityUrl)}&showTitle=false",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _appendToken(String url) {
|
String _appendToken(String url) {
|
||||||
final token = AccountStorage().getToken();
|
final token = AccountStorage().getToken();
|
||||||
if (token.isEmpty) return url;
|
if (token.isEmpty) return url;
|
||||||
@ -497,14 +348,6 @@ class _MePage2State extends State<MePage2> with WidgetsBindingObserver {
|
|||||||
iconPath: 'sc_images/index/sc_icon_bag.png',
|
iconPath: 'sc_images/index/sc_icon_bag.png',
|
||||||
onTap: () => SCNavigatorUtils.push(context, StoreRoute.bags),
|
onTap: () => SCNavigatorUtils.push(context, StoreRoute.bags),
|
||||||
),
|
),
|
||||||
SizedBox(width: 10.w),
|
|
||||||
_buildEntryItem(
|
|
||||||
title: 'cp',
|
|
||||||
iconPath: 'sc_images/index/sc_icon_cp_entry.png',
|
|
||||||
iconWidth: 47,
|
|
||||||
iconHeight: 50,
|
|
||||||
onTap: _openCpActivity,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/dialog/dialog_base.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
@ -13,6 +15,7 @@ import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repositor
|
|||||||
import 'package:yumi/shared/business_logic/models/res/bags_list_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/bags_list_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/bag/props_bag_chatbox_detail_dialog.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
||||||
import 'package:yumi/modules/store/store_route.dart';
|
import 'package:yumi/modules/store/store_route.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
||||||
@ -152,15 +155,53 @@ class _BagsChatboxPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final selected = selecteChatbox;
|
if (myChatbox?.id !=
|
||||||
if (selected == null) {
|
selecteChatbox?.propsResources?.id) {
|
||||||
return;
|
///佩戴
|
||||||
}
|
SmartDialog.show(
|
||||||
_use(
|
tag: "showConfirmDialog",
|
||||||
selected,
|
alignment: Alignment.center,
|
||||||
myChatbox?.id == selected.propsResources?.id,
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteChatbox!, false);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
///卸下
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUnUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteChatbox!, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -235,18 +276,33 @@ class _BagsChatboxPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
bottom: 5.w,
|
||||||
|
end: 5.w,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
selecteChatbox = res;
|
||||||
|
setState(() {});
|
||||||
|
_showDetail(res);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(5.w),
|
||||||
|
child: Image.asset(
|
||||||
|
"sc_images/store/sc_icon_shop_item_search.png",
|
||||||
|
width: 18.w,
|
||||||
|
height: 18.w,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
selecteChatbox = res;
|
selecteChatbox = res;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
if (myChatbox?.id != res.propsResources?.id) {
|
|
||||||
final success = await _use(res, false);
|
|
||||||
if (!success || !mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
showStoreBagFullScreenResourcePreview(
|
||||||
context,
|
context,
|
||||||
resource: res.propsResources,
|
resource: res.propsResources,
|
||||||
@ -282,42 +338,46 @@ class _BagsChatboxPageState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _use(BagsListRes res, bool unload) async {
|
void _showDetail(BagsListRes res) {
|
||||||
SCLoadingManager.show(context: context);
|
SmartDialog.show(
|
||||||
final localizations = SCAppLocalizations.of(context)!;
|
tag: "showPropsDetail",
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
alignment: Alignment.center,
|
||||||
context,
|
animationType: SmartAnimationType.fade,
|
||||||
listen: false,
|
builder: (_) {
|
||||||
|
return PropsBagChatboxDetailDialog(res);
|
||||||
|
},
|
||||||
|
onDismiss: () {
|
||||||
|
myChatbox = AccountStorage().getChatbox();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
try {
|
}
|
||||||
await SCStoreRepositoryImp().switchPropsUse(
|
|
||||||
|
void _use(BagsListRes res, bool unload) {
|
||||||
|
SCLoadingManager.show(context: context);
|
||||||
|
SCStoreRepositoryImp()
|
||||||
|
.switchPropsUse(
|
||||||
SCPropsType.CHAT_BUBBLE.name,
|
SCPropsType.CHAT_BUBBLE.name,
|
||||||
res.propsResources?.id ?? "",
|
res.propsResources?.id ?? "",
|
||||||
unload,
|
unload,
|
||||||
);
|
)
|
||||||
try {
|
.then((value) async {
|
||||||
await profileManager.fetchUserProfileData(loadGuardCount: false);
|
setState(() {});
|
||||||
} catch (_) {}
|
|
||||||
if (!mounted) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
myChatbox = res.propsResources;
|
myChatbox = res.propsResources;
|
||||||
SCTts.show(localizations.successfulWear);
|
SCTts.show(SCAppLocalizations.of(context)!.successfulWear);
|
||||||
} else {
|
} else {
|
||||||
myChatbox = null;
|
myChatbox = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(SCAppLocalizations.of(context)!.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
profileManager.syncCurrentUserChatBubble(
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
unload ? null : res.propsResources,
|
context,
|
||||||
expireTime: res.expireTime?.toString(),
|
listen: false,
|
||||||
);
|
).fetchUserProfileData(loadGuardCount: false);
|
||||||
setState(() {});
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
}
|
})
|
||||||
|
.catchError((e) {
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
@ -11,12 +12,14 @@ import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart';
|
|||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
|
||||||
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/dialog/dialog_base.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.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/store/store_bag_page_helpers.dart';
|
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
||||||
|
|
||||||
@ -164,11 +167,49 @@ class BagsDataCardPageState
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final selected = selectedDataCard;
|
if (!selectedInUse) {
|
||||||
if (selected == null) {
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selectedDataCard!, false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_use(selected, selectedInUse);
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUnUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selectedDataCard!, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -247,29 +288,33 @@ class BagsDataCardPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
bottom: 5.w,
|
||||||
|
end: 5.w,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
selectedDataCard = res;
|
||||||
|
setState(() {});
|
||||||
|
_showDetail(res);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(5.w),
|
||||||
|
child: Image.asset(
|
||||||
|
"sc_images/store/sc_icon_shop_item_search.png",
|
||||||
|
width: 18.w,
|
||||||
|
height: 18.w,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
selectedDataCard = res;
|
selectedDataCard = res;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
final expired =
|
|
||||||
(res.expireTime ?? 0) < DateTime.now().millisecondsSinceEpoch;
|
|
||||||
if (expired) {
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
|
||||||
context,
|
|
||||||
resource: res.propsResources,
|
|
||||||
previewKind: SCStoreItemPreviewKind.dataCard,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (myDataCard?.id != res.propsResources?.id) {
|
|
||||||
final success = await _use(res, false);
|
|
||||||
if (!success || !mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
showStoreBagFullScreenResourcePreview(
|
||||||
context,
|
context,
|
||||||
resource: res.propsResources,
|
resource: res.propsResources,
|
||||||
@ -303,22 +348,40 @@ class BagsDataCardPageState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _use(BagsListRes res, bool unload) async {
|
void _showDetail(BagsListRes res) {
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showPropsDetail",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return PropsBagDataCardDetailDialog(res);
|
||||||
|
},
|
||||||
|
onDismiss: () {
|
||||||
|
myDataCard = AccountStorage().getDataCard();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _use(BagsListRes res, bool unload) {
|
||||||
SCLoadingManager.show(context: context);
|
SCLoadingManager.show(context: context);
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
final localizations = SCAppLocalizations.of(context)!;
|
final localizations = SCAppLocalizations.of(context)!;
|
||||||
try {
|
SCStoreRepositoryImp()
|
||||||
await SCStoreRepositoryImp().switchPropsUse(
|
.switchPropsUse(
|
||||||
SCPropsType.DATA_CARD.name,
|
SCPropsType.DATA_CARD.name,
|
||||||
res.propsResources?.id ?? "",
|
res.propsResources?.id ?? "",
|
||||||
unload,
|
unload,
|
||||||
);
|
)
|
||||||
|
.then((value) async {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return false;
|
SCLoadingManager.hide();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
setState(() {});
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
myDataCard = res.propsResources;
|
myDataCard = res.propsResources;
|
||||||
SCTts.show(localizations.successfulWear);
|
SCTts.show(localizations.successfulWear);
|
||||||
@ -326,13 +389,11 @@ class BagsDataCardPageState
|
|||||||
myDataCard = null;
|
myDataCard = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(localizations.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
setState(() {});
|
|
||||||
profileManager.fetchUserProfileData(loadGuardCount: false);
|
profileManager.fetchUserProfileData(loadGuardCount: false);
|
||||||
return true;
|
|
||||||
} catch (_) {
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
}
|
})
|
||||||
|
.catchError((e) {
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
||||||
@ -10,11 +11,13 @@ import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
|||||||
import 'package:yumi/modules/store/store_route.dart';
|
import 'package:yumi/modules/store/store_route.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/dialog/dialog_base.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/bag/props_bag_headdress_detail_dialog.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/store/store_bag_page_helpers.dart';
|
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -152,15 +155,53 @@ class _BagsHeaddressPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final selected = selecteHeaddress;
|
if (myHeaddress?.id !=
|
||||||
if (selected == null) {
|
selecteHeaddress?.propsResources?.id) {
|
||||||
return;
|
///佩戴
|
||||||
}
|
SmartDialog.show(
|
||||||
_use(
|
tag: "showConfirmDialog",
|
||||||
selected,
|
alignment: Alignment.center,
|
||||||
myHeaddress?.id == selected.propsResources?.id,
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteHeaddress!, false);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
///卸下
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUnUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteHeaddress!, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -235,18 +276,33 @@ class _BagsHeaddressPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PositionedDirectional(
|
||||||
|
bottom: 5.w,
|
||||||
|
end: 5.w,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
selecteHeaddress = res;
|
||||||
|
setState(() {});
|
||||||
|
_showDetail(res);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(5.w),
|
||||||
|
child: Image.asset(
|
||||||
|
"sc_images/store/sc_icon_shop_item_search.png",
|
||||||
|
width: 13.w,
|
||||||
|
height: 13.w,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
selecteHeaddress = res;
|
selecteHeaddress = res;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
if (myHeaddress?.id != res.propsResources?.id) {
|
|
||||||
final success = await _use(res, false);
|
|
||||||
if (!success || !mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
showStoreBagFullScreenResourcePreview(
|
||||||
context,
|
context,
|
||||||
resource: res.propsResources,
|
resource: res.propsResources,
|
||||||
@ -283,36 +339,46 @@ class _BagsHeaddressPageState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _use(BagsListRes res, bool unload) async {
|
void _showDetail(BagsListRes res) {
|
||||||
SCLoadingManager.show(context: context);
|
SmartDialog.show(
|
||||||
final localizations = SCAppLocalizations.of(context)!;
|
tag: "showPropsDetail",
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
alignment: Alignment.center,
|
||||||
context,
|
animationType: SmartAnimationType.fade,
|
||||||
listen: false,
|
builder: (_) {
|
||||||
|
return PropsBagHeaddressDetailDialog(res);
|
||||||
|
},
|
||||||
|
onDismiss: () {
|
||||||
|
myHeaddress = AccountStorage().getHeaddress();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
try {
|
}
|
||||||
await SCStoreRepositoryImp().switchPropsUse(
|
|
||||||
|
void _use(BagsListRes res, bool unload) {
|
||||||
|
SCLoadingManager.show(context: context);
|
||||||
|
SCStoreRepositoryImp()
|
||||||
|
.switchPropsUse(
|
||||||
SCPropsType.AVATAR_FRAME.name,
|
SCPropsType.AVATAR_FRAME.name,
|
||||||
res.propsResources?.id ?? "",
|
res.propsResources?.id ?? "",
|
||||||
unload,
|
unload,
|
||||||
);
|
)
|
||||||
if (!mounted) {
|
.then((value) async {
|
||||||
return false;
|
setState(() {});
|
||||||
}
|
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
myHeaddress = res.propsResources;
|
myHeaddress = res.propsResources;
|
||||||
SCTts.show(localizations.successfulWear);
|
SCTts.show(SCAppLocalizations.of(context)!.successfulWear);
|
||||||
} else {
|
} else {
|
||||||
myHeaddress = null;
|
myHeaddress = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(SCAppLocalizations.of(context)!.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
setState(() {});
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
profileManager.fetchUserProfileData(loadGuardCount: false);
|
context,
|
||||||
return true;
|
listen: false,
|
||||||
} catch (e) {
|
).fetchUserProfileData(loadGuardCount: false);
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
}
|
})
|
||||||
|
.catchError((e) {
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/bag/props_bag_mountains_detail_dialog.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/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
@ -19,6 +21,7 @@ import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
|||||||
|
|
||||||
import '../../../../shared/data_sources/models/enum/sc_props_type.dart';
|
import '../../../../shared/data_sources/models/enum/sc_props_type.dart';
|
||||||
import '../../../../ui_kit/components/sc_debounce_widget.dart';
|
import '../../../../ui_kit/components/sc_debounce_widget.dart';
|
||||||
|
import '../../../../ui_kit/components/dialog/dialog_base.dart';
|
||||||
import '../../../../ui_kit/theme/socialchat_theme.dart';
|
import '../../../../ui_kit/theme/socialchat_theme.dart';
|
||||||
|
|
||||||
///坐骑
|
///坐骑
|
||||||
@ -158,15 +161,53 @@ class _BagsMountainsPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final selected = selecteMountains;
|
if (myMountains?.id !=
|
||||||
if (selected == null) {
|
selecteMountains?.propsResources?.id) {
|
||||||
return;
|
///佩戴
|
||||||
}
|
SmartDialog.show(
|
||||||
_use(
|
tag: "showConfirmDialog",
|
||||||
selected,
|
alignment: Alignment.center,
|
||||||
myMountains?.id == selected.propsResources?.id,
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteMountains!, false);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
///卸下
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUnUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteMountains!, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -244,15 +285,9 @@ class _BagsMountainsPageState
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
selecteMountains = res;
|
selecteMountains = res;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
if (myMountains?.id != res.propsResources?.id) {
|
|
||||||
final success = await _use(res, false);
|
|
||||||
if (!success || !mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
showStoreBagFullScreenResourcePreview(
|
||||||
context,
|
context,
|
||||||
resource: res.propsResources,
|
resource: res.propsResources,
|
||||||
@ -288,36 +323,46 @@ class _BagsMountainsPageState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _use(BagsListRes res, bool unload) async {
|
void _showDetail(BagsListRes res) {
|
||||||
SCLoadingManager.show(context: context);
|
SmartDialog.show(
|
||||||
final localizations = SCAppLocalizations.of(context)!;
|
tag: "showPropsDetail",
|
||||||
final profileManager = Provider.of<SocialChatUserProfileManager>(
|
alignment: Alignment.center,
|
||||||
context,
|
animationType: SmartAnimationType.fade,
|
||||||
listen: false,
|
builder: (_) {
|
||||||
|
return PropsBagMountainsDetailDialog(res);
|
||||||
|
},
|
||||||
|
onDismiss: () {
|
||||||
|
myMountains = AccountStorage().getMountains();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
try {
|
}
|
||||||
await SCStoreRepositoryImp().switchPropsUse(
|
|
||||||
|
void _use(BagsListRes res, bool unload) {
|
||||||
|
SCLoadingManager.show(context: context);
|
||||||
|
SCStoreRepositoryImp()
|
||||||
|
.switchPropsUse(
|
||||||
SCPropsType.RIDE.name,
|
SCPropsType.RIDE.name,
|
||||||
res.propsResources?.id ?? "",
|
res.propsResources?.id ?? "",
|
||||||
unload,
|
unload,
|
||||||
);
|
)
|
||||||
if (!mounted) {
|
.then((value) async {
|
||||||
return false;
|
setState(() {});
|
||||||
}
|
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
myMountains = res.propsResources;
|
myMountains = res.propsResources;
|
||||||
SCTts.show(localizations.successfulWear);
|
SCTts.show(SCAppLocalizations.of(context)!.successfulWear);
|
||||||
} else {
|
} else {
|
||||||
myMountains = null;
|
myMountains = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(SCAppLocalizations.of(context)!.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
setState(() {});
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
profileManager.fetchUserProfileData(loadGuardCount: false);
|
context,
|
||||||
return true;
|
listen: false,
|
||||||
} catch (e) {
|
).fetchUserProfileData(loadGuardCount: false);
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
}
|
})
|
||||||
|
.catchError((e) {
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
|||||||
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
import 'package:yumi/ui_kit/components/sc_page_list.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart';
|
||||||
|
import 'package:yumi/ui_kit/widgets/bag/props_bag_theme_detail_dialog.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
||||||
import 'package:yumi/modules/store/store_route.dart';
|
import 'package:yumi/modules/store/store_route.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -19,6 +20,7 @@ import '../../../../shared/business_logic/models/res/sc_room_theme_list_res.dart
|
|||||||
import '../../../../services/audio/rtc_manager.dart';
|
import '../../../../services/audio/rtc_manager.dart';
|
||||||
import '../../../../services/audio/rtm_manager.dart';
|
import '../../../../services/audio/rtm_manager.dart';
|
||||||
import '../../../../ui_kit/components/sc_debounce_widget.dart';
|
import '../../../../ui_kit/components/sc_debounce_widget.dart';
|
||||||
|
import '../../../../ui_kit/components/dialog/dialog_base.dart';
|
||||||
import '../../../../ui_kit/components/sc_tts.dart';
|
import '../../../../ui_kit/components/sc_tts.dart';
|
||||||
import '../../../../ui_kit/widgets/room/room_msg_item.dart';
|
import '../../../../ui_kit/widgets/room/room_msg_item.dart';
|
||||||
|
|
||||||
@ -147,11 +149,51 @@ class _BagsThemePageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final selected = selecteTheme;
|
if (!(selecteTheme?.useTheme ?? false)) {
|
||||||
if (selected == null) {
|
///佩戴
|
||||||
return;
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteTheme!, false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
///卸下
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showConfirmDialog",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
debounce: true,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return MsgDialog(
|
||||||
|
title: SCAppLocalizations.of(context)!.tips,
|
||||||
|
msg:
|
||||||
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.confirmUnUseTips,
|
||||||
|
btnText:
|
||||||
|
SCAppLocalizations.of(context)!.confirm,
|
||||||
|
onEnsure: () {
|
||||||
|
_use(selecteTheme!, true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_use(selected, selected.useTheme ?? false);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -230,15 +272,9 @@ class _BagsThemePageState
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
selecteTheme = res;
|
selecteTheme = res;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
if (!(res.useTheme ?? false)) {
|
|
||||||
final success = await _use(res, false);
|
|
||||||
if (!success || !mounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showStoreBagFullScreenResourcePreview(
|
showStoreBagFullScreenResourcePreview(
|
||||||
context,
|
context,
|
||||||
sourceUrl: res.themeBack,
|
sourceUrl: res.themeBack,
|
||||||
@ -272,46 +308,62 @@ class _BagsThemePageState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _use(SCRoomThemeListRes res, bool unload) async {
|
void _use(SCRoomThemeListRes res, bool unload) {
|
||||||
SCLoadingManager.show();
|
SCLoadingManager.show();
|
||||||
final localizations = SCAppLocalizations.of(context)!;
|
SCStoreRepositoryImp()
|
||||||
final rtcManager = Provider.of<RealTimeCommunicationManager>(
|
.themeSwitchUse(res.id ?? "", unload)
|
||||||
context,
|
.then((value) async {
|
||||||
listen: false,
|
SCLoadingManager.hide();
|
||||||
);
|
|
||||||
final rtmProvider = Provider.of<RtmProvider>(context, listen: false);
|
|
||||||
try {
|
|
||||||
await SCStoreRepositoryImp().themeSwitchUse(res.id ?? "", unload);
|
|
||||||
if (!mounted) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
SmartDialog.dismiss(tag: "showPropsDetail");
|
SmartDialog.dismiss(tag: "showPropsDetail");
|
||||||
if (!unload) {
|
if (!unload) {
|
||||||
SCTts.show(localizations.successfulWear);
|
SCTts.show(SCAppLocalizations.of(context)!.successfulWear);
|
||||||
rtcManager.updateRoomBG(res);
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).updateRoomBG(res);
|
||||||
} else {
|
} else {
|
||||||
rtcManager.updateRoomBG(SCRoomThemeListRes());
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).updateRoomBG(SCRoomThemeListRes());
|
||||||
selecteTheme = null;
|
selecteTheme = null;
|
||||||
SCTts.show(localizations.successfullyUnloaded);
|
SCTts.show(SCAppLocalizations.of(context)!.successfullyUnloaded);
|
||||||
}
|
}
|
||||||
loadData(1);
|
loadData(1);
|
||||||
|
|
||||||
///发送一条消息更新远端房间信息
|
///发送一条消息更新远端房间信息
|
||||||
String? groupId =
|
String? groupId =
|
||||||
rtcManager.currenRoom?.roomProfile?.roomProfile?.roomAccount;
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).currenRoom?.roomProfile?.roomProfile?.roomAccount;
|
||||||
if (groupId != null) {
|
if (groupId != null) {
|
||||||
Msg msg = Msg(
|
Msg msg = Msg(
|
||||||
groupId: groupId,
|
groupId: groupId,
|
||||||
msg: unload ? "" : jsonEncode(res.toJson()),
|
msg: unload ? "" : jsonEncode(res.toJson()),
|
||||||
type: SCRoomMsgType.roomBGUpdate,
|
type: SCRoomMsgType.roomBGUpdate,
|
||||||
);
|
);
|
||||||
rtmProvider.dispatchMessage(msg, addLocal: false);
|
Provider.of<RtmProvider>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).dispatchMessage(msg, addLocal: false);
|
||||||
}
|
}
|
||||||
return true;
|
})
|
||||||
} catch (e) {
|
.catchError((e) {
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showDetail(SCRoomThemeListRes res) {
|
||||||
|
SmartDialog.show(
|
||||||
|
tag: "showPropsDetail",
|
||||||
|
alignment: Alignment.center,
|
||||||
|
animationType: SmartAnimationType.fade,
|
||||||
|
builder: (_) {
|
||||||
|
return PropsBagThemeDetailDialog(res, () {
|
||||||
|
loadData(1);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
|||||||
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
|
import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart';
|
|
||||||
|
|
||||||
class VipDetailPage extends StatefulWidget {
|
class VipDetailPage extends StatefulWidget {
|
||||||
const VipDetailPage({super.key});
|
const VipDetailPage({super.key});
|
||||||
@ -471,7 +470,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
|
|
||||||
Widget _buildLevelCard() {
|
Widget _buildLevelCard() {
|
||||||
final assetLevel = _assetLevel;
|
final assetLevel = _assetLevel;
|
||||||
final effectImage = _selectedEffectImage;
|
final backgroundCard = _selectedBackgroundCard;
|
||||||
|
final effectImage = _selectedEffectImage ?? _selectedLongBadge;
|
||||||
return Center(
|
return Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 351.w,
|
width: 351.w,
|
||||||
@ -482,7 +482,10 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
child: _buildVipCardBackground(assetLevel: assetLevel),
|
child: _buildVipCardBackground(
|
||||||
|
resource: backgroundCard,
|
||||||
|
assetLevel: assetLevel,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@ -522,7 +525,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_hasResourceUrl(effectImage))
|
|
||||||
PositionedDirectional(
|
PositionedDirectional(
|
||||||
end: 24.w,
|
end: 24.w,
|
||||||
top: -36.w,
|
top: -36.w,
|
||||||
@ -539,13 +541,25 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildVipCardBackground({required int assetLevel}) {
|
Widget _buildVipCardBackground({
|
||||||
return Image.asset(
|
required SCVipResourceRes? resource,
|
||||||
|
required int assetLevel,
|
||||||
|
}) {
|
||||||
|
final fallback = Image.asset(
|
||||||
'sc_images/vip/sc_vip_card_bg_$assetLevel.png',
|
'sc_images/vip/sc_vip_card_bg_$assetLevel.png',
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
);
|
);
|
||||||
|
if (!_hasResourceUrl(resource)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return SCDataCardResourceView(
|
||||||
|
sourceUrl: resource?.sourceResourceUrl,
|
||||||
|
coverUrl: resource?.previewUrl,
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
fallback: fallback,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLevelDivider() {
|
Widget _buildLevelDivider() {
|
||||||
@ -618,28 +632,20 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDecorationGrid(List<_VipFeatureData> items) {
|
Widget _buildDecorationGrid(List<_VipFeatureData> items) {
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
final spacing = 10.w;
|
|
||||||
final maxWidth =
|
|
||||||
constraints.maxWidth.isFinite ? constraints.maxWidth : 314.w;
|
|
||||||
final itemWidth = (maxWidth - spacing * 2) / 3;
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
spacing: spacing,
|
spacing: 10.w,
|
||||||
runSpacing: spacing,
|
runSpacing: 10.w,
|
||||||
children:
|
children:
|
||||||
items
|
items
|
||||||
.map(
|
.map(
|
||||||
(item) => _buildFeatureTile(
|
(item) => _buildFeatureTile(
|
||||||
item,
|
item,
|
||||||
width: itemWidth,
|
width: item.wide ? 206.w : 98.w,
|
||||||
height: 100.w,
|
height: 100.w,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPrivilegeGrid(List<_VipFeatureData> items) {
|
Widget _buildPrivilegeGrid(List<_VipFeatureData> items) {
|
||||||
@ -766,7 +772,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
resource: _selectedAvatarFrame,
|
resource: _selectedAvatarFrame,
|
||||||
fallbackTitle: 'Avatar Frame',
|
fallbackTitle: 'Avatar Frame',
|
||||||
fallbackIcon: Icons.person_pin_circle_outlined,
|
fallbackIcon: Icons.person_pin_circle_outlined,
|
||||||
previewKind: SCStoreItemPreviewKind.avatarFrame,
|
|
||||||
);
|
);
|
||||||
_addResourceItem(
|
_addResourceItem(
|
||||||
items,
|
items,
|
||||||
@ -785,14 +790,14 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
resource: _selectedEntryEffect,
|
resource: _selectedEntryEffect,
|
||||||
fallbackTitle: 'Entry Effect',
|
fallbackTitle: 'Entry Effect',
|
||||||
fallbackIcon: Icons.airline_seat_recline_extra,
|
fallbackIcon: Icons.airline_seat_recline_extra,
|
||||||
|
wide: true,
|
||||||
);
|
);
|
||||||
_addResourceItem(
|
_addResourceItem(
|
||||||
items,
|
items,
|
||||||
resource: _selectedBackgroundCard,
|
resource: _selectedBackgroundCard,
|
||||||
fallbackTitle: 'Profile Card',
|
fallbackTitle: 'Profile Card',
|
||||||
fallbackIcon: Icons.badge_outlined,
|
fallbackIcon: Icons.badge_outlined,
|
||||||
previewKind: SCStoreItemPreviewKind.dataCard,
|
wide: true,
|
||||||
previewFit: BoxFit.cover,
|
|
||||||
);
|
);
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -810,9 +815,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
required String fallbackTitle,
|
required String fallbackTitle,
|
||||||
IconData? fallbackIcon,
|
IconData? fallbackIcon,
|
||||||
bool wide = false,
|
bool wide = false,
|
||||||
SCStoreItemPreviewKind previewKind = SCStoreItemPreviewKind.resource,
|
|
||||||
BoxFit previewFit = BoxFit.contain,
|
|
||||||
BoxFit tileFit = BoxFit.contain,
|
|
||||||
}) {
|
}) {
|
||||||
if (!_hasResource(resource)) {
|
if (!_hasResource(resource)) {
|
||||||
return;
|
return;
|
||||||
@ -822,12 +824,8 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
_resourceTitle(resource, fallbackTitle),
|
_resourceTitle(resource, fallbackTitle),
|
||||||
null,
|
null,
|
||||||
icon: fallbackIcon,
|
icon: fallbackIcon,
|
||||||
sourceUrl: resource?.sourceResourceUrl,
|
resourceUrl: resource?.previewUrl,
|
||||||
coverUrl: _resourceCoverUrl(resource),
|
|
||||||
wide: wide,
|
wide: wide,
|
||||||
previewKind: previewKind,
|
|
||||||
previewFit: previewFit,
|
|
||||||
tileFit: tileFit,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -848,18 +846,6 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
return fallbackTitle;
|
return fallbackTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _resourceCoverUrl(SCVipResourceRes? resource) {
|
|
||||||
final coverUrl = resource?.coverUrl?.trim();
|
|
||||||
if (coverUrl != null && coverUrl.isNotEmpty) {
|
|
||||||
return coverUrl;
|
|
||||||
}
|
|
||||||
final cover = resource?.cover?.trim();
|
|
||||||
if (cover != null && cover.isNotEmpty) {
|
|
||||||
return cover;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildDynamicResourceVisual({
|
Widget _buildDynamicResourceVisual({
|
||||||
required SCVipResourceRes? resource,
|
required SCVipResourceRes? resource,
|
||||||
required double width,
|
required double width,
|
||||||
@ -960,7 +946,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFeatureIcon(_VipFeatureData item, {required double size}) {
|
Widget _buildFeatureIcon(_VipFeatureData item, {required double size}) {
|
||||||
final url = item.coverUrl?.trim();
|
final url = item.resourceUrl?.trim();
|
||||||
if (url != null && url.isNotEmpty) {
|
if (url != null && url.isNotEmpty) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(6.w),
|
borderRadius: BorderRadius.circular(6.w),
|
||||||
@ -987,15 +973,15 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFeatureVisual(_VipFeatureData item, {required bool isWide}) {
|
Widget _buildFeatureVisual(_VipFeatureData item, {required bool isWide}) {
|
||||||
final coverUrl = item.coverUrl?.trim();
|
final url = item.resourceUrl?.trim();
|
||||||
if (coverUrl != null && coverUrl.isNotEmpty) {
|
if (url != null && url.isNotEmpty) {
|
||||||
return netImage(
|
return netImage(
|
||||||
url: coverUrl,
|
url: url,
|
||||||
fit: item.tileFit,
|
fit: BoxFit.contain,
|
||||||
noDefaultImg: true,
|
noDefaultImg: true,
|
||||||
errorWidget:
|
errorWidget:
|
||||||
item.assetPath != null
|
item.assetPath != null
|
||||||
? Image.asset(item.assetPath!, fit: item.tileFit)
|
? Image.asset(item.assetPath!, fit: BoxFit.contain)
|
||||||
: isWide
|
: isWide
|
||||||
? Center(child: _buildWideRewardPlaceholder())
|
? Center(child: _buildWideRewardPlaceholder())
|
||||||
: _buildRewardPlaceholder(item.icon ?? Icons.auto_awesome),
|
: _buildRewardPlaceholder(item.icon ?? Icons.auto_awesome),
|
||||||
@ -1076,22 +1062,7 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
required double height,
|
required double height,
|
||||||
}) {
|
}) {
|
||||||
final isWide = item.wide;
|
final isWide = item.wide;
|
||||||
return GestureDetector(
|
return Container(
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap:
|
|
||||||
item.canPreview
|
|
||||||
? () => showStoreBagFullScreenResourcePreview(
|
|
||||||
context,
|
|
||||||
sourceUrl: item.sourceUrl,
|
|
||||||
coverUrl: item.coverUrl,
|
|
||||||
previewKind: item.previewKind,
|
|
||||||
fit: item.previewFit,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
child: Semantics(
|
|
||||||
label: item.title,
|
|
||||||
button: item.canPreview,
|
|
||||||
child: Container(
|
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -1100,9 +1071,24 @@ class _VipDetailPageState extends State<VipDetailPage> {
|
|||||||
color: Colors.black.withValues(alpha: 0.38),
|
color: Colors.black.withValues(alpha: 0.38),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w),
|
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 8.w),
|
||||||
child: Center(child: _buildFeatureVisual(item, isWide: isWide)),
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(child: _buildFeatureVisual(item, isWide: isWide)),
|
||||||
|
SizedBox(height: 5.w),
|
||||||
|
Text(
|
||||||
|
item.title,
|
||||||
|
maxLines: 2,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 12.sp,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1434,27 +1420,15 @@ class _VipFeatureData {
|
|||||||
this.title,
|
this.title,
|
||||||
this.assetPath, {
|
this.assetPath, {
|
||||||
this.icon,
|
this.icon,
|
||||||
this.sourceUrl,
|
this.resourceUrl,
|
||||||
this.coverUrl,
|
|
||||||
this.wide = false,
|
this.wide = false,
|
||||||
this.previewKind = SCStoreItemPreviewKind.resource,
|
|
||||||
this.previewFit = BoxFit.contain,
|
|
||||||
this.tileFit = BoxFit.contain,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final String? assetPath;
|
final String? assetPath;
|
||||||
final IconData? icon;
|
final IconData? icon;
|
||||||
final String? sourceUrl;
|
final String? resourceUrl;
|
||||||
final String? coverUrl;
|
|
||||||
final bool wide;
|
final bool wide;
|
||||||
final SCStoreItemPreviewKind previewKind;
|
|
||||||
final BoxFit previewFit;
|
|
||||||
final BoxFit tileFit;
|
|
||||||
|
|
||||||
bool get canPreview =>
|
|
||||||
(sourceUrl?.trim().isNotEmpty ?? false) ||
|
|
||||||
(coverUrl?.trim().isNotEmpty ?? false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VipActionButtonConfig {
|
class _VipActionButtonConfig {
|
||||||
|
|||||||
@ -37,6 +37,11 @@ class _RechargePageState extends State<RechargePage> {
|
|||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).initializePaymentProcessor(context);
|
).initializePaymentProcessor(context);
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
Provider.of<IOSPaymentProcessor>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).initializePaymentProcessor(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +60,7 @@ class _RechargePageState extends State<RechargePage> {
|
|||||||
.getRechargePageScaffoldBackgroundColor(),
|
.getRechargePageScaffoldBackgroundColor(),
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
appBar: SocialChatStandardAppBar(
|
appBar: SocialChatStandardAppBar(
|
||||||
title: SCAppLocalizations.of(context)!.wallet,
|
title: SCAppLocalizations.of(context)!.recharge,
|
||||||
actions: [
|
actions: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: _showRecordDialog,
|
onTap: _showRecordDialog,
|
||||||
@ -78,10 +83,7 @@ class _RechargePageState extends State<RechargePage> {
|
|||||||
_buildWalletSection(),
|
_buildWalletSection(),
|
||||||
SizedBox(height: 36.w),
|
SizedBox(height: 36.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child:
|
child: Padding(
|
||||||
Platform.isIOS
|
|
||||||
? const SizedBox.shrink()
|
|
||||||
: Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(12.w, 8.w, 12.w, 0),
|
padding: EdgeInsets.fromLTRB(12.w, 8.w, 12.w, 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -89,10 +91,24 @@ class _RechargePageState extends State<RechargePage> {
|
|||||||
_buildMethodSection(),
|
_buildMethodSection(),
|
||||||
SizedBox(height: 14.w),
|
SizedBox(height: 14.w),
|
||||||
Expanded(child: _buildNativeProductList()),
|
Expanded(child: _buildNativeProductList()),
|
||||||
|
if (Platform.isIOS) ...[
|
||||||
SizedBox(height: 12.w),
|
SizedBox(height: 12.w),
|
||||||
_buildFooterButton(
|
_buildFooterButton(
|
||||||
title:
|
title:
|
||||||
SCAppLocalizations.of(context)!.recharge,
|
SCAppLocalizations.of(
|
||||||
|
context,
|
||||||
|
)!.restorePurchases,
|
||||||
|
onTap: () {
|
||||||
|
Provider.of<IOSPaymentProcessor>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).recoverTransactions();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
SizedBox(height: 12.w),
|
||||||
|
_buildFooterButton(
|
||||||
|
title: SCAppLocalizations.of(context)!.recharge,
|
||||||
onTap: _processNativePurchase,
|
onTap: _processNativePurchase,
|
||||||
),
|
),
|
||||||
SizedBox(height: 14.w),
|
SizedBox(height: 14.w),
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import 'package:yumi/app/constants/sc_screen.dart';
|
|||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/tools/sc_deviceId_utils.dart';
|
import 'package:yumi/shared/tools/sc_deviceId_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_h5_url_utils.dart';
|
import 'package:yumi/shared/tools/sc_h5_url_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_room_top_layer_guard.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
||||||
import 'package:yumi/shared/tools/sc_url_launcher_utils.dart';
|
import 'package:yumi/shared/tools/sc_url_launcher_utils.dart';
|
||||||
import 'package:yumi/main.dart';
|
import 'package:yumi/main.dart';
|
||||||
@ -41,7 +40,6 @@ class WebViewPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _WebViewPageState extends State<WebViewPage> {
|
class _WebViewPageState extends State<WebViewPage> {
|
||||||
late WebViewController _controller;
|
late WebViewController _controller;
|
||||||
late final SCRoomTopLayerGuardLease _topLayerGuardLease;
|
|
||||||
String _title = "";
|
String _title = "";
|
||||||
double _progress = 0.0;
|
double _progress = 0.0;
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
@ -50,7 +48,6 @@ class _WebViewPageState extends State<WebViewPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_topLayerGuardLease = SCRoomTopLayerGuard().acquire(reason: 'webview_page');
|
|
||||||
urlLink = SCH5UrlUtils.appendAppParams(widget.url);
|
urlLink = SCH5UrlUtils.appendAppParams(widget.url);
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
debugPrint('[WebViewPage] load url=${_maskSensitiveUrl(urlLink)}');
|
debugPrint('[WebViewPage] load url=${_maskSensitiveUrl(urlLink)}');
|
||||||
@ -180,7 +177,6 @@ class _WebViewPageState extends State<WebViewPage> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller.loadRequest(Uri.parse('about:blank'));
|
_controller.loadRequest(Uri.parse('about:blank'));
|
||||||
_topLayerGuardLease.release();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,11 +20,9 @@ class RoomRtcEnterRoomConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RoomRtcAnchorConfig {
|
class RoomRtcAnchorConfig {
|
||||||
const RoomRtcAnchorConfig({required this.muted, bool? publishMuted})
|
const RoomRtcAnchorConfig({required this.muted});
|
||||||
: publishMuted = publishMuted ?? muted;
|
|
||||||
|
|
||||||
final bool muted;
|
final bool muted;
|
||||||
final bool publishMuted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoomRtcAudioMixingConfig {
|
class RoomRtcAudioMixingConfig {
|
||||||
|
|||||||
@ -34,7 +34,6 @@ abstract class RoomTrtcAudioEffectManager {
|
|||||||
void resumePlayMusic(int id);
|
void resumePlayMusic(int id);
|
||||||
void setMusicPublishVolume(int id, int volume);
|
void setMusicPublishVolume(int id, int volume);
|
||||||
void setMusicPlayoutVolume(int id, int volume);
|
void setMusicPlayoutVolume(int id, int volume);
|
||||||
void setVoiceCaptureVolume(int volume);
|
|
||||||
int getMusicCurrentPosInMS(int id);
|
int getMusicCurrentPosInMS(int id);
|
||||||
int getMusicDurationInMS(String path);
|
int getMusicDurationInMS(String path);
|
||||||
void seekMusicToPosInTime(int id, int pts);
|
void seekMusicToPosInTime(int id, int pts);
|
||||||
@ -157,11 +156,6 @@ class TencentRoomTrtcAudioEffectManager implements RoomTrtcAudioEffectManager {
|
|||||||
_manager.setMusicPlayoutVolume(id, volume);
|
_manager.setMusicPlayoutVolume(id, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void setVoiceCaptureVolume(int volume) {
|
|
||||||
_manager.setVoiceCaptureVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int getMusicCurrentPosInMS(int id) {
|
int getMusicCurrentPosInMS(int id) {
|
||||||
return _manager.getMusicCurrentPosInMS(id);
|
return _manager.getMusicCurrentPosInMS(id);
|
||||||
@ -370,8 +364,7 @@ class TrtcRoomRtcEngineAdapter implements RoomRtcEngineAdapter {
|
|||||||
try {
|
try {
|
||||||
cloud.switchRole(TRTCRoleType.anchor);
|
cloud.switchRole(TRTCRoleType.anchor);
|
||||||
cloud.startLocalAudio(TRTCAudioQuality.defaultMode);
|
cloud.startLocalAudio(TRTCAudioQuality.defaultMode);
|
||||||
_musicManager?.setVoiceCaptureVolume(config.muted ? 0 : 100);
|
cloud.muteLocalAudio(config.muted);
|
||||||
cloud.muteLocalAudio(config.publishMuted);
|
|
||||||
_isLocalAudioStarted = true;
|
_isLocalAudioStarted = true;
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:yumi/app_localizations.dart';
|
import 'package:yumi/app_localizations.dart';
|
||||||
@ -14,17 +12,8 @@ import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
|||||||
import 'package:yumi/shared/business_logic/models/res/room_user_card_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/room_user_card_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_user_identity_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_user_identity_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_vip_res.dart';
|
||||||
import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_level_utils.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_cp_relation_notice_utils.dart';
|
|
||||||
|
|
||||||
class SocialChatUserProfileManager extends ChangeNotifier {
|
class SocialChatUserProfileManager extends ChangeNotifier {
|
||||||
static const List<String> _cpProfileRelationTypes = [
|
|
||||||
'CP',
|
|
||||||
'BROTHER',
|
|
||||||
'SISTERS',
|
|
||||||
];
|
|
||||||
|
|
||||||
SCUserProfileCmd? editUser;
|
SCUserProfileCmd? editUser;
|
||||||
|
|
||||||
void updateUserAvatar(String avatar) {
|
void updateUserAvatar(String avatar) {
|
||||||
@ -87,38 +76,9 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
currentUser.setUserProfile(profile);
|
currentUser.setUserProfile(profile);
|
||||||
AccountStorage().setCurrentUser(currentUser);
|
AccountStorage().setCurrentUser(currentUser);
|
||||||
}
|
}
|
||||||
if ((userProfile?.id ?? "").isNotEmpty && userProfile?.id == profile.id) {
|
|
||||||
userProfile = profile;
|
|
||||||
}
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncCurrentUserChatBubble(
|
|
||||||
PropsResources? chatBubble, {
|
|
||||||
String? expireTime,
|
|
||||||
}) {
|
|
||||||
final profile = currentUserProfile;
|
|
||||||
if (profile == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final useProps = <UseProps>[
|
|
||||||
...?profile.useProps?.where(
|
|
||||||
(item) => item.propsResources?.type != SCPropsType.CHAT_BUBBLE.name,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
if (chatBubble != null) {
|
|
||||||
useProps.add(
|
|
||||||
UseProps(
|
|
||||||
expireTime: expireTime ?? "4102444800000",
|
|
||||||
propsResources: chatBubble,
|
|
||||||
userId: profile.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
syncCurrentUserProfile(profile.copyWith(useProps: useProps));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future fetchUserProfileData({
|
Future fetchUserProfileData({
|
||||||
bool loadGuardCount = true,
|
bool loadGuardCount = true,
|
||||||
bool refreshFamilyData = false,
|
bool refreshFamilyData = false,
|
||||||
@ -130,7 +90,6 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
var userInfo = await SCAccountRepository().loadUserInfo(userId);
|
var userInfo = await SCAccountRepository().loadUserInfo(userId);
|
||||||
userInfo = await _withCurrentVipLevel(userInfo, userId);
|
userInfo = await _withCurrentVipLevel(userInfo, userId);
|
||||||
userInfo = await _withCpCabinProfiles(userInfo, userId);
|
|
||||||
syncCurrentUserProfile(userInfo);
|
syncCurrentUserProfile(userInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,208 +120,25 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
|
|
||||||
/// 当前正在查看的个人详情资料,不用于承载登录用户自己的 Me 页数据。
|
/// 当前正在查看的个人详情资料,不用于承载登录用户自己的 Me 页数据。
|
||||||
SocialChatUserProfile? userProfile;
|
SocialChatUserProfile? userProfile;
|
||||||
Object? userProfileLoadError;
|
|
||||||
bool isUserProfileLoading = false;
|
|
||||||
int _userInfoRequestSerial = 0;
|
|
||||||
|
|
||||||
Future<void> getUserInfoById(String userId) {
|
void getUserInfoById(String userId) async {
|
||||||
return _loadUserInfoById(userId, clearBeforeLoad: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> reloadUserInfoById(String userId) {
|
|
||||||
return _loadUserInfoById(userId, clearBeforeLoad: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _loadUserInfoById(
|
|
||||||
String userId, {
|
|
||||||
required bool clearBeforeLoad,
|
|
||||||
}) async {
|
|
||||||
final requestSerial = ++_userInfoRequestSerial;
|
|
||||||
final normalizedUserId = userId.trim();
|
|
||||||
if (clearBeforeLoad) {
|
|
||||||
userProfile = null;
|
userProfile = null;
|
||||||
}
|
final loadedProfile = await SCAccountRepository().loadUserInfo(userId);
|
||||||
userProfileLoadError = null;
|
userProfile = await _withCurrentVipLevel(loadedProfile, userId);
|
||||||
isUserProfileLoading = true;
|
|
||||||
notifyListeners();
|
|
||||||
|
|
||||||
if (normalizedUserId.isEmpty) {
|
|
||||||
if (requestSerial == _userInfoRequestSerial) {
|
|
||||||
userProfileLoadError = ArgumentError('userId is empty');
|
|
||||||
isUserProfileLoading = false;
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final loadedProfile = await SCAccountRepository().loadUserInfo(
|
|
||||||
normalizedUserId,
|
|
||||||
);
|
|
||||||
if (requestSerial != _userInfoRequestSerial) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
userProfile = loadedProfile;
|
|
||||||
userProfileLoadError = null;
|
|
||||||
isUserProfileLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
unawaited(
|
|
||||||
_hydrateUserInfoById(
|
|
||||||
profile: loadedProfile,
|
|
||||||
userId: normalizedUserId,
|
|
||||||
requestSerial: requestSerial,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
if (requestSerial != _userInfoRequestSerial) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
userProfile = null;
|
|
||||||
userProfileLoadError = error;
|
|
||||||
_debugProfileLoadFailure(
|
|
||||||
scope: 'detail',
|
|
||||||
userId: normalizedUserId,
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
if (requestSerial == _userInfoRequestSerial && isUserProfileLoading) {
|
|
||||||
isUserProfileLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///房间资料卡
|
///房间资料卡
|
||||||
RoomUserCardRes? userCardInfo;
|
RoomUserCardRes? userCardInfo;
|
||||||
Object? userCardLoadError;
|
|
||||||
bool isUserCardLoading = false;
|
|
||||||
bool isUserCardRelationLoading = false;
|
|
||||||
int _userCardRequestSerial = 0;
|
|
||||||
int _userCardSessionSerial = 0;
|
|
||||||
int? _activeUserCardSessionSerial;
|
|
||||||
|
|
||||||
// List<UserCountGuardRes>? userCountGuardResList;
|
// List<UserCountGuardRes>? userCountGuardResList;
|
||||||
|
|
||||||
int beginRoomUserCardSession() {
|
void roomUserCard(String roomId, String userId) async {
|
||||||
final sessionSerial = ++_userCardSessionSerial;
|
|
||||||
_activeUserCardSessionSerial = sessionSerial;
|
|
||||||
return sessionSerial;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RoomUserCardRes?> roomUserCard(
|
|
||||||
String roomId,
|
|
||||||
String userId, {
|
|
||||||
int? sessionSerial,
|
|
||||||
}) async {
|
|
||||||
final activeSessionSerial = sessionSerial ?? beginRoomUserCardSession();
|
|
||||||
_activeUserCardSessionSerial = activeSessionSerial;
|
|
||||||
final requestSerial = ++_userCardRequestSerial;
|
|
||||||
final normalizedRoomId = roomId.trim();
|
|
||||||
final normalizedUserId = userId.trim();
|
|
||||||
userCardInfo = null;
|
userCardInfo = null;
|
||||||
userCardLoadError = null;
|
userCardInfo = await SCChatRoomRepository().roomUserCard(roomId, userId);
|
||||||
isUserCardLoading = true;
|
userCardInfo = await _withCurrentVipLevelForCard(userCardInfo, userId);
|
||||||
isUserCardRelationLoading = false;
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
if (normalizedUserId.isEmpty) {
|
|
||||||
if (requestSerial == _userCardRequestSerial) {
|
|
||||||
userCardLoadError = ArgumentError('userId is empty');
|
|
||||||
isUserCardLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final card = await _loadHydratedRoomUserCard(
|
|
||||||
roomId: normalizedRoomId,
|
|
||||||
userId: normalizedUserId,
|
|
||||||
);
|
|
||||||
if (requestSerial != _userCardRequestSerial ||
|
|
||||||
activeSessionSerial != _activeUserCardSessionSerial) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
userCardInfo = card;
|
|
||||||
userCardLoadError = null;
|
|
||||||
isUserCardLoading = false;
|
|
||||||
isUserCardRelationLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
return card;
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
if (requestSerial != _userCardRequestSerial) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
userCardInfo = null;
|
|
||||||
userCardLoadError = error;
|
|
||||||
isUserCardRelationLoading = false;
|
|
||||||
_debugProfileLoadFailure(
|
|
||||||
scope: 'roomCard',
|
|
||||||
userId: normalizedUserId,
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
if (requestSerial == _userCardRequestSerial && isUserCardLoading) {
|
|
||||||
isUserCardLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RoomUserCardRes?> preloadRoomUserCardForOpen(
|
|
||||||
String roomId,
|
|
||||||
String userId, {
|
|
||||||
int? sessionSerial,
|
|
||||||
}) {
|
|
||||||
return roomUserCard(roomId, userId, sessionSerial: sessionSerial);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<RoomUserCardRes?> _loadHydratedRoomUserCard({
|
|
||||||
required String roomId,
|
|
||||||
required String userId,
|
|
||||||
}) async {
|
|
||||||
final card = await SCChatRoomRepository().roomUserCard(roomId, userId);
|
|
||||||
return _withCurrentVipLevelForCard(card, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _hydrateUserInfoById({
|
|
||||||
required SocialChatUserProfile profile,
|
|
||||||
required String userId,
|
|
||||||
required int requestSerial,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
var mergedProfile = await _withCurrentVipLevel(profile, userId);
|
|
||||||
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
|
|
||||||
if (requestSerial != _userInfoRequestSerial) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
userProfile = mergedProfile;
|
|
||||||
userProfileLoadError = null;
|
|
||||||
notifyListeners();
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
_debugProfileLoadFailure(
|
|
||||||
scope: 'detailHydrate',
|
|
||||||
userId: userId,
|
|
||||||
error: error,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearRoomUserCard({int? sessionSerial}) {
|
|
||||||
if (sessionSerial != null &&
|
|
||||||
sessionSerial != _activeUserCardSessionSerial) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_userCardRequestSerial++;
|
|
||||||
_activeUserCardSessionSerial = null;
|
|
||||||
userCardInfo = null;
|
|
||||||
userCardLoadError = null;
|
|
||||||
isUserCardLoading = false;
|
|
||||||
isUserCardRelationLoading = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RoomUserCardRes?> _withCurrentVipLevelForCard(
|
Future<RoomUserCardRes?> _withCurrentVipLevelForCard(
|
||||||
@ -373,373 +149,13 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
if (card == null || profile == null) {
|
if (card == null || profile == null) {
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
var mergedProfile = await _withCurrentVipLevel(profile, userId);
|
final mergedProfile = await _withCurrentVipLevel(profile, userId);
|
||||||
mergedProfile = await _withCpCabinProfiles(mergedProfile, userId);
|
|
||||||
if (mergedProfile == profile) {
|
if (mergedProfile == profile) {
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
return card.copyWith(userProfile: mergedProfile);
|
return card.copyWith(userProfile: mergedProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _debugProfileLoadFailure({
|
|
||||||
required String scope,
|
|
||||||
required String userId,
|
|
||||||
required Object error,
|
|
||||||
required StackTrace stackTrace,
|
|
||||||
}) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[ProfileLoad][$scope] failed userId=$userId error=$error\n$stackTrace',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SocialChatUserProfile> _withCpCabinProfiles(
|
|
||||||
SocialChatUserProfile profile,
|
|
||||||
String userId,
|
|
||||||
) async {
|
|
||||||
if (userId.trim().isEmpty) {
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
final fallbackCpPairs = <CPRes>[];
|
|
||||||
final fallbackCloseFriendPairs = <CPRes>[];
|
|
||||||
_addCpRelationPairs(
|
|
||||||
cpPairs: fallbackCpPairs,
|
|
||||||
closeFriendPairs: fallbackCloseFriendPairs,
|
|
||||||
relations: profile.cpList,
|
|
||||||
fallbackRelationType: 'CP',
|
|
||||||
);
|
|
||||||
_addCpRelationPairs(
|
|
||||||
cpPairs: fallbackCpPairs,
|
|
||||||
closeFriendPairs: fallbackCloseFriendPairs,
|
|
||||||
relations: profile.closeFriendList,
|
|
||||||
);
|
|
||||||
final cpPairs = <CPRes>[];
|
|
||||||
final closeFriendPairs = <CPRes>[];
|
|
||||||
_addCpRelationPairs(
|
|
||||||
cpPairs: cpPairs,
|
|
||||||
closeFriendPairs: closeFriendPairs,
|
|
||||||
relations: fallbackCpPairs,
|
|
||||||
);
|
|
||||||
_addCpRelationPairs(
|
|
||||||
cpPairs: cpPairs,
|
|
||||||
closeFriendPairs: closeFriendPairs,
|
|
||||||
relations: fallbackCloseFriendPairs,
|
|
||||||
);
|
|
||||||
var loadedAnyCabin = false;
|
|
||||||
if (kDebugMode) {
|
|
||||||
final currentUserId =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] source userId=$userId currentUserId=$currentUserId '
|
|
||||||
'profileCp=${profile.cpList?.length ?? 0} '
|
|
||||||
'profileCloseFriends=${profile.closeFriendList?.length ?? 0}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await Future.wait(
|
|
||||||
_cpProfileRelationTypes.map((relationType) async {
|
|
||||||
final pairs = await _safeLoadCpRelationshipPairs(userId, relationType);
|
|
||||||
_addCpRelationPairs(
|
|
||||||
cpPairs: cpPairs,
|
|
||||||
closeFriendPairs: closeFriendPairs,
|
|
||||||
relations: pairs,
|
|
||||||
fallbackRelationType: relationType,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
await Future.wait(
|
|
||||||
_cpProfileRelationTypes.map((relationType) async {
|
|
||||||
final cabin = await _safeLoadCpCabin(userId, relationType);
|
|
||||||
if (cabin != null) {
|
|
||||||
loadedAnyCabin = true;
|
|
||||||
}
|
|
||||||
final cpPair = cabin?.cpPairUserProfileCO;
|
|
||||||
if (cpPair == null || !_hasCpPair(cpPair)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final normalizedType = _normalizeCpRelationType(
|
|
||||||
cpPair.relationType ?? relationType,
|
|
||||||
);
|
|
||||||
final cabinLevelText = scCpRelationLevelTextFromCabin(
|
|
||||||
cabin,
|
|
||||||
fallbackText: cpPair.levelText,
|
|
||||||
);
|
|
||||||
final rightsLevelText = await _safeLoadCurrentUserCpLevelText(
|
|
||||||
userId: userId,
|
|
||||||
cpPair: cpPair,
|
|
||||||
relationType: normalizedType,
|
|
||||||
fallbackText: cabinLevelText,
|
|
||||||
);
|
|
||||||
final enrichedPair = cpPair.copyWith(
|
|
||||||
relationType: normalizedType,
|
|
||||||
cpValue: cpPair.cpValue ?? cabin?.cpVal?.toDouble(),
|
|
||||||
levelText: rightsLevelText ?? cabinLevelText,
|
|
||||||
dismissCost: cpPair.dismissCost ?? _cpDismissCostFromCabin(cabin),
|
|
||||||
);
|
|
||||||
_addCpRelationPair(
|
|
||||||
cpPairs: cpPairs,
|
|
||||||
closeFriendPairs: closeFriendPairs,
|
|
||||||
relation: enrichedPair,
|
|
||||||
fallbackRelationType: normalizedType,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] hydrate userId=$userId cp=${cpPairs.length} '
|
|
||||||
'closeFriends=${closeFriendPairs.length}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!loadedAnyCabin) {
|
|
||||||
return profile.copyWith(
|
|
||||||
cpList: cpPairs,
|
|
||||||
closeFriendList: closeFriendPairs,
|
|
||||||
isCpRelation: cpPairs.isNotEmpty,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return profile.copyWith(
|
|
||||||
cpList: cpPairs,
|
|
||||||
closeFriendList: closeFriendPairs,
|
|
||||||
isCpRelation: cpPairs.isNotEmpty,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addCpRelationPairs({
|
|
||||||
required List<CPRes> cpPairs,
|
|
||||||
required List<CPRes> closeFriendPairs,
|
|
||||||
required List<CPRes>? relations,
|
|
||||||
String? fallbackRelationType,
|
|
||||||
}) {
|
|
||||||
for (final relation in relations ?? const <CPRes>[]) {
|
|
||||||
_addCpRelationPair(
|
|
||||||
cpPairs: cpPairs,
|
|
||||||
closeFriendPairs: closeFriendPairs,
|
|
||||||
relation: relation,
|
|
||||||
fallbackRelationType: fallbackRelationType,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addCpRelationPair({
|
|
||||||
required List<CPRes> cpPairs,
|
|
||||||
required List<CPRes> closeFriendPairs,
|
|
||||||
required CPRes relation,
|
|
||||||
String? fallbackRelationType,
|
|
||||||
}) {
|
|
||||||
if (!_hasCpPair(relation)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final relationTypeSource =
|
|
||||||
(relation.relationType?.trim().isNotEmpty ?? false)
|
|
||||||
? relation.relationType
|
|
||||||
: fallbackRelationType;
|
|
||||||
if ((relationTypeSource ?? "").trim().isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final normalizedType = _normalizeCpRelationType(relationTypeSource);
|
|
||||||
final normalizedRelation = relation.copyWith(relationType: normalizedType);
|
|
||||||
final target = normalizedType == 'CP' ? cpPairs : closeFriendPairs;
|
|
||||||
final relationKey = _cpRelationPairKey(normalizedRelation);
|
|
||||||
final existingIndex = target.indexWhere(
|
|
||||||
(item) => _cpRelationPairKey(item) == relationKey,
|
|
||||||
);
|
|
||||||
if (existingIndex >= 0) {
|
|
||||||
target[existingIndex] = normalizedRelation;
|
|
||||||
} else {
|
|
||||||
target.add(normalizedRelation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _cpRelationPairKey(CPRes relation) {
|
|
||||||
final type = _normalizeCpRelationType(relation.relationType);
|
|
||||||
final userIds = <String>[
|
|
||||||
relation.meUserId?.trim() ?? "",
|
|
||||||
relation.cpUserId?.trim() ?? "",
|
|
||||||
]..removeWhere((value) => value.isEmpty);
|
|
||||||
if (userIds.length >= 2) {
|
|
||||||
userIds.sort();
|
|
||||||
return '$type|id|${userIds.join("|")}';
|
|
||||||
}
|
|
||||||
final accounts = <String>[
|
|
||||||
relation.meAccount?.trim() ?? "",
|
|
||||||
relation.cpAccount?.trim() ?? "",
|
|
||||||
]..removeWhere((value) => value.isEmpty);
|
|
||||||
if (accounts.length >= 2) {
|
|
||||||
accounts.sort();
|
|
||||||
return '$type|account|${accounts.join("|")}';
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
type,
|
|
||||||
relation.meUserId?.trim() ?? "",
|
|
||||||
relation.meAccount?.trim() ?? "",
|
|
||||||
relation.cpUserId?.trim() ?? "",
|
|
||||||
relation.cpAccount?.trim() ?? "",
|
|
||||||
relation.cpUserNickname?.trim() ?? "",
|
|
||||||
relation.meUserNickname?.trim() ?? "",
|
|
||||||
].join('|');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<CPRes>> _safeLoadCpRelationshipPairs(
|
|
||||||
String userId,
|
|
||||||
String relationType,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
final pairs = await SCAccountRepository().cpRelationshipPairs(
|
|
||||||
relationType: relationType,
|
|
||||||
userId: userId,
|
|
||||||
);
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] pair loaded userId=$userId relationType=$relationType '
|
|
||||||
'count=${pairs.length}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return pairs;
|
|
||||||
} catch (error) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] pair failed userId=$userId relationType=$relationType error=$error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const <CPRes>[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SCCpCabinRes?> _safeLoadCpCabin(
|
|
||||||
String userId,
|
|
||||||
String relationType,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
final cabin = await SCAccountRepository().cpCabin(
|
|
||||||
userId,
|
|
||||||
relationType: relationType,
|
|
||||||
);
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] cabin loaded userId=$userId relationType=$relationType '
|
|
||||||
'hasPair=${_hasCpPair(cabin.cpPairUserProfileCO)} cpVal=${cabin.cpVal}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return cabin;
|
|
||||||
} catch (error) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] cabin failed userId=$userId '
|
|
||||||
'relationType=$relationType error=$error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _safeLoadCurrentUserCpLevelText({
|
|
||||||
required String userId,
|
|
||||||
required CPRes cpPair,
|
|
||||||
required String relationType,
|
|
||||||
String? fallbackText,
|
|
||||||
}) async {
|
|
||||||
final partnerUserId = _cpPartnerUserIdForCurrentUser(cpPair);
|
|
||||||
if (partnerUserId.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final config = await SCAccountRepository().cpRightsConfig(
|
|
||||||
relationType: relationType,
|
|
||||||
cpUserId: partnerUserId,
|
|
||||||
);
|
|
||||||
return scCpRelationLevelTextFromRightsConfig(
|
|
||||||
config,
|
|
||||||
fallbackText: fallbackText,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (kDebugMode) {
|
|
||||||
debugPrint(
|
|
||||||
'[CP][Profile] rightsConfig failed userId=$userId '
|
|
||||||
'partnerUserId=$partnerUserId relationType=$relationType '
|
|
||||||
'error=$error',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _cpPartnerUserIdForCurrentUser(CPRes cpPair) {
|
|
||||||
final currentUserId =
|
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
|
||||||
if (currentUserId.isEmpty) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
final meUserId = cpPair.meUserId?.trim() ?? "";
|
|
||||||
final cpUserId = cpPair.cpUserId?.trim() ?? "";
|
|
||||||
if (currentUserId == meUserId) {
|
|
||||||
return cpUserId;
|
|
||||||
}
|
|
||||||
if (currentUserId == cpUserId) {
|
|
||||||
return meUserId;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _hasCpPair(CPRes? pair) {
|
|
||||||
return (pair?.cpUserId?.trim().isNotEmpty ?? false) ||
|
|
||||||
(pair?.cpUserAvatar?.trim().isNotEmpty ?? false) ||
|
|
||||||
(pair?.cpUserNickname?.trim().isNotEmpty ?? false) ||
|
|
||||||
(pair?.meUserId?.trim().isNotEmpty ?? false) ||
|
|
||||||
(pair?.meUserAvatar?.trim().isNotEmpty ?? false) ||
|
|
||||||
(pair?.meUserNickname?.trim().isNotEmpty ?? false);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _normalizeCpRelationType(String? relationType) {
|
|
||||||
return scNormalizeCpRelationType(relationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
int? _cpDismissCostFromCabin(SCCpCabinRes? cabin) {
|
|
||||||
final directCost = cabin?.dismissCost;
|
|
||||||
if (directCost != null) {
|
|
||||||
return directCost;
|
|
||||||
}
|
|
||||||
final config = cabin?.currentCpCabinConfigCO;
|
|
||||||
if (config is! Map) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return _cpIntValue(
|
|
||||||
_firstCpConfigValue(config, const [
|
|
||||||
"dismissCost",
|
|
||||||
"dismissGold",
|
|
||||||
"dismissCoins",
|
|
||||||
"dismissCoin",
|
|
||||||
"cancelCost",
|
|
||||||
"cancelGold",
|
|
||||||
"removeCost",
|
|
||||||
"partWaysCost",
|
|
||||||
"consumeGold",
|
|
||||||
"goldCost",
|
|
||||||
"cost",
|
|
||||||
"coins",
|
|
||||||
"priceGold",
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic _firstCpConfigValue(Map source, List<String> keys) {
|
|
||||||
for (final key in keys) {
|
|
||||||
if (source.containsKey(key) && source[key] != null) {
|
|
||||||
return source[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int? _cpIntValue(dynamic value) {
|
|
||||||
if (value is int) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (value is num) {
|
|
||||||
return value.toInt();
|
|
||||||
}
|
|
||||||
return int.tryParse(value?.toString() ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<SocialChatUserProfile> _withCurrentVipLevel(
|
Future<SocialChatUserProfile> _withCurrentVipLevel(
|
||||||
SocialChatUserProfile profile,
|
SocialChatUserProfile profile,
|
||||||
String userId,
|
String userId,
|
||||||
@ -777,12 +193,10 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _isCurrentUserId(String userId) {
|
bool _isCurrentUserId(String userId) {
|
||||||
final currentUserId =
|
final currentUserId = AccountStorage().getCurrentUser()?.userProfile?.id;
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id?.trim();
|
|
||||||
final normalizedUserId = userId.trim();
|
|
||||||
return currentUserId != null &&
|
return currentUserId != null &&
|
||||||
currentUserId.isNotEmpty &&
|
currentUserId.isNotEmpty &&
|
||||||
currentUserId == normalizedUserId;
|
currentUserId == userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
Future<SCVipStatusRes?> _loadCurrentVipStatus() async {
|
||||||
@ -904,7 +318,7 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> cpRlationshipProcessApply(
|
void cpRlationshipProcessApply(
|
||||||
BuildContext ct,
|
BuildContext ct,
|
||||||
String id,
|
String id,
|
||||||
bool status,
|
bool status,
|
||||||
@ -912,183 +326,8 @@ class SocialChatUserProfileManager extends ChangeNotifier {
|
|||||||
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
|
final successMessage = SCAppLocalizations.of(ct)!.operationSuccessful;
|
||||||
try {
|
try {
|
||||||
await SCAccountRepository().cpRelationshipProcessApply(id, status);
|
await SCAccountRepository().cpRelationshipProcessApply(id, status);
|
||||||
await fetchUserProfileData(loadGuardCount: false);
|
fetchUserProfileData(loadGuardCount: false);
|
||||||
await refreshLoadedCpProfiles();
|
|
||||||
SCTts.show(successMessage);
|
SCTts.show(successMessage);
|
||||||
return true;
|
} catch (e) {}
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> refreshLoadedCpProfiles() async {
|
|
||||||
await _refreshLoadedCpProfilesForUserIds(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> refreshLoadedCpProfilesForUsers(Set<String> userIds) async {
|
|
||||||
final normalizedUserIds =
|
|
||||||
userIds.map((userId) => userId.trim()).where((userId) {
|
|
||||||
return userId.isNotEmpty;
|
|
||||||
}).toSet();
|
|
||||||
if (normalizedUserIds.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await _refreshLoadedCpProfilesForUserIds(normalizedUserIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeLoadedCpRelation({
|
|
||||||
required String userId,
|
|
||||||
required String peerUserId,
|
|
||||||
required String relationType,
|
|
||||||
}) {
|
|
||||||
final normalizedUserId = userId.trim();
|
|
||||||
final normalizedPeerUserId = peerUserId.trim();
|
|
||||||
final normalizedRelationType = _normalizeCpRelationType(relationType);
|
|
||||||
if (normalizedUserId.isEmpty ||
|
|
||||||
normalizedPeerUserId.isEmpty ||
|
|
||||||
normalizedRelationType.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed = false;
|
|
||||||
final currentProfile = currentUserProfile;
|
|
||||||
if (currentProfile != null) {
|
|
||||||
final nextCurrentProfile = _profileWithoutCpRelation(
|
|
||||||
currentProfile,
|
|
||||||
userId: normalizedUserId,
|
|
||||||
peerUserId: normalizedPeerUserId,
|
|
||||||
relationType: normalizedRelationType,
|
|
||||||
);
|
|
||||||
if (!identical(nextCurrentProfile, currentProfile)) {
|
|
||||||
final currentUser = AccountStorage().getCurrentUser();
|
|
||||||
currentUser?.setUserProfile(nextCurrentProfile);
|
|
||||||
if (currentUser != null) {
|
|
||||||
AccountStorage().setCurrentUser(currentUser);
|
|
||||||
}
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final detailProfile = userProfile;
|
|
||||||
if (detailProfile != null) {
|
|
||||||
final nextDetailProfile = _profileWithoutCpRelation(
|
|
||||||
detailProfile,
|
|
||||||
userId: normalizedUserId,
|
|
||||||
peerUserId: normalizedPeerUserId,
|
|
||||||
relationType: normalizedRelationType,
|
|
||||||
);
|
|
||||||
if (!identical(nextDetailProfile, detailProfile)) {
|
|
||||||
userProfile = nextDetailProfile;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final cardProfile = userCardInfo?.userProfile;
|
|
||||||
if (cardProfile != null) {
|
|
||||||
final nextCardProfile = _profileWithoutCpRelation(
|
|
||||||
cardProfile,
|
|
||||||
userId: normalizedUserId,
|
|
||||||
peerUserId: normalizedPeerUserId,
|
|
||||||
relationType: normalizedRelationType,
|
|
||||||
);
|
|
||||||
if (!identical(nextCardProfile, cardProfile)) {
|
|
||||||
userCardInfo = userCardInfo?.copyWith(userProfile: nextCardProfile);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SocialChatUserProfile _profileWithoutCpRelation(
|
|
||||||
SocialChatUserProfile profile, {
|
|
||||||
required String userId,
|
|
||||||
required String peerUserId,
|
|
||||||
required String relationType,
|
|
||||||
}) {
|
|
||||||
var changed = false;
|
|
||||||
List<CPRes>? filterRelations(List<CPRes>? relations) {
|
|
||||||
if (relations == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final filtered =
|
|
||||||
relations.where((relation) {
|
|
||||||
return !_matchesCpRelation(
|
|
||||||
relation,
|
|
||||||
userId: userId,
|
|
||||||
peerUserId: peerUserId,
|
|
||||||
relationType: relationType,
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
if (filtered.length != relations.length) {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
return filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
final nextCpList = filterRelations(profile.cpList);
|
|
||||||
final nextCloseFriendList = filterRelations(profile.closeFriendList);
|
|
||||||
if (!changed) {
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
return profile.copyWith(
|
|
||||||
cpList: nextCpList,
|
|
||||||
closeFriendList: nextCloseFriendList,
|
|
||||||
isCpRelation: nextCpList?.isNotEmpty == true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _matchesCpRelation(
|
|
||||||
CPRes relation, {
|
|
||||||
required String userId,
|
|
||||||
required String peerUserId,
|
|
||||||
required String relationType,
|
|
||||||
}) {
|
|
||||||
if (_normalizeCpRelationType(relation.relationType) != relationType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final relationUserIds = <String>{
|
|
||||||
relation.meUserId?.trim() ?? "",
|
|
||||||
relation.cpUserId?.trim() ?? "",
|
|
||||||
}..remove("");
|
|
||||||
return relationUserIds.contains(userId) &&
|
|
||||||
relationUserIds.contains(peerUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _refreshLoadedCpProfilesForUserIds(Set<String>? userIds) async {
|
|
||||||
String refreshedUserId(String? userId) {
|
|
||||||
final normalizedUserId = userId?.trim() ?? "";
|
|
||||||
if (normalizedUserId.isEmpty) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return userIds == null || userIds.contains(normalizedUserId)
|
|
||||||
? normalizedUserId
|
|
||||||
: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentProfile = currentUserProfile;
|
|
||||||
final currentUserId = refreshedUserId(currentProfile?.id);
|
|
||||||
if (currentProfile != null && currentUserId.isNotEmpty) {
|
|
||||||
syncCurrentUserProfile(
|
|
||||||
await _withCpCabinProfiles(currentProfile, currentUserId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final detailUserId = refreshedUserId(userProfile?.id);
|
|
||||||
if (userProfile != null && detailUserId.isNotEmpty) {
|
|
||||||
userProfile = await _withCpCabinProfiles(userProfile!, detailUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final cardProfile = userCardInfo?.userProfile;
|
|
||||||
final cardUserId = refreshedUserId(cardProfile?.id);
|
|
||||||
if (userCardInfo != null && cardProfile != null && cardUserId.isNotEmpty) {
|
|
||||||
userCardInfo = userCardInfo?.copyWith(
|
|
||||||
userProfile: await _withCpCabinProfiles(cardProfile, cardUserId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import 'package:yumi/shared/business_logic/models/res/sc_banner_leaderboard_res.
|
|||||||
import 'package:yumi/shared/business_logic/models/res/country_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/country_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_backpack_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/gift_backpack_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_first_recharge_reward_res.dart';
|
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_index_banner_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_index_banner_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_room_emoji_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_room_emoji_res.dart';
|
||||||
|
|
||||||
@ -37,9 +36,6 @@ class SCAppGeneralManager extends ChangeNotifier {
|
|||||||
bool _hasGiftListLoaded = false;
|
bool _hasGiftListLoaded = false;
|
||||||
bool _isFetchingGiftBackpack = false;
|
bool _isFetchingGiftBackpack = false;
|
||||||
bool _hasGiftBackpackLoaded = false;
|
bool _hasGiftBackpackLoaded = false;
|
||||||
bool _isFetchingFirstRechargeRewardHome = false;
|
|
||||||
bool _hasFirstRechargeRewardHomeLoaded = false;
|
|
||||||
SCFirstRechargeRewardHomeRes? _firstRechargeRewardHome;
|
|
||||||
|
|
||||||
Map<String, List<SocialChatGiftRes>> giftByTab = {};
|
Map<String, List<SocialChatGiftRes>> giftByTab = {};
|
||||||
Map<String, List<SocialChatGiftRes>> activityGiftByTab = {};
|
Map<String, List<SocialChatGiftRes>> activityGiftByTab = {};
|
||||||
@ -63,40 +59,10 @@ class SCAppGeneralManager extends ChangeNotifier {
|
|||||||
bool get isGiftBackpackLoading =>
|
bool get isGiftBackpackLoading =>
|
||||||
_isFetchingGiftBackpack && !_hasGiftBackpackLoaded;
|
_isFetchingGiftBackpack && !_hasGiftBackpackLoaded;
|
||||||
|
|
||||||
bool get shouldShowFirstRechargeReward =>
|
|
||||||
_firstRechargeRewardHome?.shouldShowRewardDialog ?? false;
|
|
||||||
|
|
||||||
SCFirstRechargeRewardHomeRes? get firstRechargeRewardHome =>
|
|
||||||
_firstRechargeRewardHome;
|
|
||||||
|
|
||||||
bool isGiftTabLoading(String type) {
|
bool isGiftTabLoading(String type) {
|
||||||
return type == backpackGiftTab ? isGiftBackpackLoading : isGiftListLoading;
|
return type == backpackGiftTab ? isGiftBackpackLoading : isGiftListLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SCFirstRechargeRewardHomeRes?> refreshFirstRechargeRewardHome({
|
|
||||||
bool forceRefresh = false,
|
|
||||||
}) async {
|
|
||||||
if (_isFetchingFirstRechargeRewardHome) {
|
|
||||||
return _firstRechargeRewardHome;
|
|
||||||
}
|
|
||||||
if (_hasFirstRechargeRewardHomeLoaded && !forceRefresh) {
|
|
||||||
return _firstRechargeRewardHome;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isFetchingFirstRechargeRewardHome = true;
|
|
||||||
try {
|
|
||||||
final home = await SCConfigRepositoryImp().firstRechargeRewardHome();
|
|
||||||
_firstRechargeRewardHome = home;
|
|
||||||
_hasFirstRechargeRewardHomeLoaded = true;
|
|
||||||
return home;
|
|
||||||
} catch (_) {
|
|
||||||
return _firstRechargeRewardHome;
|
|
||||||
} finally {
|
|
||||||
_isFetchingFirstRechargeRewardHome = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///加载国家列表
|
///加载国家列表
|
||||||
Future<void> fetchCountryList() async {
|
Future<void> fetchCountryList() async {
|
||||||
if (_isFetchingCountryList) {
|
if (_isFetchingCountryList) {
|
||||||
@ -213,28 +179,24 @@ class SCAppGeneralManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _displayGiftTab(String? giftTab) {
|
|
||||||
return giftTab == "EXCLUSIVE" ? "CUSTOMIZED" : (giftTab ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rebuildGiftTabs({required bool includeCustomized}) {
|
void _rebuildGiftTabs({required bool includeCustomized}) {
|
||||||
giftByTab.clear();
|
giftByTab.clear();
|
||||||
_giftByIdMap.clear();
|
_giftByIdMap.clear();
|
||||||
_giftByStandardIdMap.clear();
|
_giftByStandardIdMap.clear();
|
||||||
for (var gift in giftResList) {
|
for (var gift in giftResList) {
|
||||||
final tab = _displayGiftTab(gift.giftTab);
|
giftByTab[gift.giftTab];
|
||||||
var gmap = giftByTab[tab];
|
var gmap = giftByTab[gift.giftTab];
|
||||||
gmap ??= [];
|
gmap ??= [];
|
||||||
gmap.add(gift);
|
gmap.add(gift);
|
||||||
giftByTab[tab] = gmap;
|
giftByTab[gift.giftTab!] = gmap;
|
||||||
var gAllMap = giftByTab["ALL"];
|
var gAllMap = giftByTab["ALL"];
|
||||||
gAllMap ??= [];
|
gAllMap ??= [];
|
||||||
if (tab != "NSCIONAL_FLAG" &&
|
if (gift.giftTab != "NSCIONAL_FLAG" &&
|
||||||
tab != "ACTIVITY" &&
|
gift.giftTab != "ACTIVITY" &&
|
||||||
tab != "LUCKY_GIFT" &&
|
gift.giftTab != "LUCKY_GIFT" &&
|
||||||
tab != "CP" &&
|
gift.giftTab != "CP" &&
|
||||||
tab != "CUSTOMIZED" &&
|
gift.giftTab != "CUSTOMIZED" &&
|
||||||
tab != "MAGIC") {
|
gift.giftTab != "MAGIC") {
|
||||||
gAllMap.add(gift);
|
gAllMap.add(gift);
|
||||||
}
|
}
|
||||||
giftByTab["ALL"] = gAllMap;
|
giftByTab["ALL"] = gAllMap;
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
|
||||||
import 'package:just_audio/just_audio.dart';
|
|
||||||
import 'package:on_audio_query/on_audio_query.dart';
|
import 'package:on_audio_query/on_audio_query.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:yumi/shared/data_sources/models/sc_music_folder_mode.dart';
|
import 'package:yumi/shared/data_sources/models/sc_music_folder_mode.dart';
|
||||||
import 'package:yumi/shared/data_sources/models/sc_music_mode.dart';
|
import 'package:yumi/shared/data_sources/models/sc_music_mode.dart';
|
||||||
import 'package:yumi/shared/tools/sc_permission_utils.dart';
|
import 'package:yumi/shared/tools/sc_permission_utils.dart';
|
||||||
@ -19,10 +15,6 @@ class LocalMusicScanner {
|
|||||||
Future<List<SCMusicFolderMode>> scanMp3Folders({
|
Future<List<SCMusicFolderMode>> scanMp3Folders({
|
||||||
List<SCMusicMode> addedSongs = const [],
|
List<SCMusicMode> addedSongs = const [],
|
||||||
}) async {
|
}) async {
|
||||||
if (Platform.isIOS) {
|
|
||||||
return _pickIosMp3Files(addedSongs: addedSongs);
|
|
||||||
}
|
|
||||||
|
|
||||||
final hasPermission = await SCPermissionUtils.checkAudioPermission();
|
final hasPermission = await SCPermissionUtils.checkAudioPermission();
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
return const <SCMusicFolderMode>[];
|
return const <SCMusicFolderMode>[];
|
||||||
@ -104,98 +96,6 @@ class LocalMusicScanner {
|
|||||||
return List<SCMusicFolderMode>.unmodifiable(folders);
|
return List<SCMusicFolderMode>.unmodifiable(folders);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SCMusicFolderMode>> _pickIosMp3Files({
|
|
||||||
required List<SCMusicMode> addedSongs,
|
|
||||||
}) async {
|
|
||||||
final FilePickerResult? result;
|
|
||||||
try {
|
|
||||||
result = await FilePicker.pickFiles(
|
|
||||||
type: FileType.custom,
|
|
||||||
allowedExtensions: const ['mp3'],
|
|
||||||
allowMultiple: true,
|
|
||||||
withData: false,
|
|
||||||
withReadStream: false,
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
return const <SCMusicFolderMode>[];
|
|
||||||
}
|
|
||||||
if (result == null || result.files.isEmpty) {
|
|
||||||
return const <SCMusicFolderMode>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
final addedIds = addedSongs.map((item) => item.id).toSet();
|
|
||||||
final addedPaths = addedSongs.map((item) => item.localPath).toSet();
|
|
||||||
final storageDir = await _iosRoomMusicDirectory();
|
|
||||||
final player = AudioPlayer();
|
|
||||||
final songs = <SCMusicMode>[];
|
|
||||||
final seen = <String>{};
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (final file in result.files) {
|
|
||||||
final sourcePath = (file.path ?? '').trim();
|
|
||||||
if (sourcePath.isEmpty ||
|
|
||||||
!_isMp3FileName(file.name) ||
|
|
||||||
!_fileExists(sourcePath)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final sourceId = _firstNonEmpty([
|
|
||||||
file.identifier,
|
|
||||||
'${file.name}:${file.size}',
|
|
||||||
sourcePath,
|
|
||||||
]);
|
|
||||||
if (sourceId.isEmpty || !seen.add(sourceId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final storedPath = await _copyIosPickedFile(
|
|
||||||
sourcePath: sourcePath,
|
|
||||||
fileName: file.name,
|
|
||||||
sourceId: sourceId,
|
|
||||||
storageDir: storageDir,
|
|
||||||
);
|
|
||||||
final durationMs = await _readLocalMp3DurationMs(player, storedPath);
|
|
||||||
final storedFile = File(storedPath);
|
|
||||||
final safeFileName = p.basename(storedPath);
|
|
||||||
songs.add(
|
|
||||||
SCMusicMode(
|
|
||||||
id: sourceId,
|
|
||||||
title: p.basenameWithoutExtension(file.name).trim(),
|
|
||||||
artist: '',
|
|
||||||
durationMs: durationMs,
|
|
||||||
localPath: storedPath,
|
|
||||||
contentUri: '',
|
|
||||||
folderPath: storageDir.path,
|
|
||||||
fileName: safeFileName,
|
|
||||||
size: storedFile.existsSync() ? storedFile.lengthSync() : file.size,
|
|
||||||
addedAt: DateTime.now().millisecondsSinceEpoch,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
await player.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (songs.isEmpty) {
|
|
||||||
return const <SCMusicFolderMode>[];
|
|
||||||
}
|
|
||||||
songs.sort(
|
|
||||||
(a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase()),
|
|
||||||
);
|
|
||||||
final addedCount =
|
|
||||||
songs.where((song) {
|
|
||||||
return addedIds.contains(song.id) ||
|
|
||||||
addedPaths.contains(song.localPath);
|
|
||||||
}).length;
|
|
||||||
return <SCMusicFolderMode>[
|
|
||||||
SCMusicFolderMode(
|
|
||||||
id: 'ios_file_picker',
|
|
||||||
name: 'Selected Music',
|
|
||||||
path: 'Files',
|
|
||||||
songs: List<SCMusicMode>.unmodifiable(songs),
|
|
||||||
addedCount: addedCount,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isMp3(SongModel song) {
|
bool _isMp3(SongModel song) {
|
||||||
final extension = _songString(
|
final extension = _songString(
|
||||||
song,
|
song,
|
||||||
@ -208,72 +108,6 @@ class LocalMusicScanner {
|
|||||||
_songString(song, "_data").toLowerCase().endsWith(".mp3");
|
_songString(song, "_data").toLowerCase().endsWith(".mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isMp3FileName(String fileName) {
|
|
||||||
return fileName.toLowerCase().endsWith('.mp3');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Directory> _iosRoomMusicDirectory() async {
|
|
||||||
final supportDir = await getApplicationSupportDirectory();
|
|
||||||
final directory = Directory(p.join(supportDir.path, 'room_music'));
|
|
||||||
if (!directory.existsSync()) {
|
|
||||||
await directory.create(recursive: true);
|
|
||||||
}
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> _copyIosPickedFile({
|
|
||||||
required String sourcePath,
|
|
||||||
required String fileName,
|
|
||||||
required String sourceId,
|
|
||||||
required Directory storageDir,
|
|
||||||
}) async {
|
|
||||||
if (p.isWithin(storageDir.path, sourcePath)) {
|
|
||||||
return sourcePath;
|
|
||||||
}
|
|
||||||
final sourceFile = File(sourcePath);
|
|
||||||
final targetFile = File(
|
|
||||||
p.join(storageDir.path, _iosStoredFileName(fileName, sourceId)),
|
|
||||||
);
|
|
||||||
if (targetFile.existsSync() &&
|
|
||||||
targetFile.lengthSync() == sourceFile.lengthSync()) {
|
|
||||||
return targetFile.path;
|
|
||||||
}
|
|
||||||
await sourceFile.copy(targetFile.path);
|
|
||||||
return targetFile.path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _iosStoredFileName(String fileName, String sourceId) {
|
|
||||||
final baseName = p.basenameWithoutExtension(fileName).trim();
|
|
||||||
final safeBaseName =
|
|
||||||
baseName
|
|
||||||
.replaceAll(RegExp(r'[^A-Za-z0-9._ -]+'), '_')
|
|
||||||
.replaceAll(RegExp(r'\s+'), ' ')
|
|
||||||
.trim();
|
|
||||||
final token = base64Url.encode(utf8.encode(sourceId)).replaceAll('=', '');
|
|
||||||
final suffix = token.length > 12 ? token.substring(0, 12) : token;
|
|
||||||
return '${safeBaseName.isEmpty ? 'music' : safeBaseName}_$suffix.mp3';
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<int> _readLocalMp3DurationMs(AudioPlayer player, String path) async {
|
|
||||||
try {
|
|
||||||
final duration = await player.setFilePath(path);
|
|
||||||
await player.stop();
|
|
||||||
return duration?.inMilliseconds ?? 0;
|
|
||||||
} catch (_) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _firstNonEmpty(List<String?> values) {
|
|
||||||
for (final value in values) {
|
|
||||||
final text = value?.trim() ?? '';
|
|
||||||
if (text.isNotEmpty) {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _fileExists(String path) {
|
bool _fileExists(String path) {
|
||||||
try {
|
try {
|
||||||
return File(path).existsSync();
|
return File(path).existsSync();
|
||||||
|
|||||||